From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.8 required=3.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,SPF_PASS,UNPARSEABLE_RELAY,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 080E4C43334 for ; Wed, 29 Aug 2018 02:55:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id A36FD20852 for ; Wed, 29 Aug 2018 02:55:37 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org A36FD20852 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=mediatek.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727622AbeH2GuL (ORCPT ); Wed, 29 Aug 2018 02:50:11 -0400 Received: from mailgw02.mediatek.com ([1.203.163.81]:19349 "EHLO mailgw02.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1725882AbeH2GuK (ORCPT ); Wed, 29 Aug 2018 02:50:10 -0400 X-UUID: 1debde76d4604a5e80d37a7f3afc0ee6-20180829 Received: from mtkcas32.mediatek.inc [(172.27.4.250)] by mailgw02.mediatek.com (envelope-from ) (mailgw01.mediatek.com ESMTP with TLS) with ESMTP id 1640217028; Wed, 29 Aug 2018 10:55:27 +0800 Received: from mtkcas09.mediatek.inc (172.21.101.178) by MTKMBS31N1.mediatek.inc (172.27.4.69) with Microsoft SMTP Server (TLS) id 15.0.1210.3; Wed, 29 Aug 2018 10:55:25 +0800 Received: from localhost.localdomain (10.17.3.153) by mtkcas09.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1210.3 via Frontend Transport; Wed, 29 Aug 2018 10:55:24 +0800 From: Chunfeng Yun To: Mathias Nyman CC: Greg Kroah-Hartman , Felipe Balbi , Matthias Brugger , Alan Stern , Chunfeng Yun , , , , , Subject: [PATCH 5/6] usb: xhci-mtk: supports bandwidth scheduling with multi-TT Date: Wed, 29 Aug 2018 10:55:17 +0800 Message-ID: <57f6f53d29d6adef7e12ed958ed29688fe37f7e0.1535510898.git.chunfeng.yun@mediatek.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <3bcc220aa54bfebc39cea54cd736388ad37ee0c5.1535510898.git.chunfeng.yun@mediatek.com> References: <3bcc220aa54bfebc39cea54cd736388ad37ee0c5.1535510898.git.chunfeng.yun@mediatek.com> MIME-Version: 1.0 Content-Type: text/plain X-MTK: N Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Supports LowSpeed and FullSpeed INT/ISOC bandwidth scheduling with USB multi-TT Signed-off-by: Chunfeng Yun --- drivers/usb/host/xhci-mtk-sch.c | 247 ++++++++++++++++++++++++++++++++++++++-- drivers/usb/host/xhci-mtk.h | 21 ++++ 2 files changed, 258 insertions(+), 10 deletions(-) diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c index 7efd890..36050a1 100644 --- a/drivers/usb/host/xhci-mtk-sch.c +++ b/drivers/usb/host/xhci-mtk-sch.c @@ -80,10 +80,98 @@ static u32 get_esit(struct xhci_ep_ctx *ep_ctx) return esit; } +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) + return NULL; /* Not below a TT */ + + /* + * Find/create our data structure. + * For hubs with a single TT, we get it directly. + * For hubs with multiple TTs, there's an extra level of pointers. + */ + tt_index = NULL; + if (utt->multi) { + tt_index = utt->hcpriv; + if (!tt_index) { /* Create the index array */ + tt_index = kcalloc(utt->hub->maxchild, + sizeof(*tt_index), GFP_KERNEL); + if (!tt_index) + return ERR_PTR(-ENOMEM); + utt->hcpriv = tt_index; + allocated_index = true; + } + port = udev->ttport - 1; + ptt = &tt_index[port]; + } else { + port = 0; + ptt = (struct mu3h_sch_tt **) &utt->hcpriv; + } + + tt = *ptt; + if (!tt) { /* Create the mu3h_sch_tt */ + tt = kzalloc(sizeof(*tt), GFP_KERNEL); + if (!tt) { + if (allocated_index) { + utt->hcpriv = NULL; + kfree(tt_index); + } + return ERR_PTR(-ENOMEM); + } + INIT_LIST_HEAD(&tt->ep_list); + tt->usb_tt = utt; + tt->tt_port = port; + *ptt = tt; + } + + return tt; +} + +/* Release the TT above udev, if it's not in use */ +static void drop_tt(struct usb_device *udev) +{ + struct usb_tt *utt = udev->tt; + struct mu3h_sch_tt *tt, **tt_index, **ptt; + int i, cnt; + + if (!utt || !utt->hcpriv) + return; /* Not below a TT, or never allocated */ + + cnt = 0; + if (utt->multi) { + tt_index = utt->hcpriv; + ptt = &tt_index[udev->ttport - 1]; + /* How many entries are left in tt_index? */ + for (i = 0; i < utt->hub->maxchild; ++i) + cnt += !!tt_index[i]; + } else { + tt_index = NULL; + ptt = (struct mu3h_sch_tt **)&utt->hcpriv; + } + + tt = *ptt; + if (!tt || !list_empty(&tt->ep_list)) + return; /* never allocated , or still in use*/ + + *ptt = NULL; + kfree(tt); + + if (cnt == 1) { + utt->hcpriv = NULL; + kfree(tt_index); + } +} + 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; @@ -101,6 +189,15 @@ static struct mu3h_sch_ep_info *create_sch_ep(struct usb_device *udev, if (!sch_ep) return ERR_PTR(-ENOMEM); + if (is_fs_or_ls(udev->speed)) { + tt = find_tt(udev); + if (IS_ERR(tt)) { + kfree(sch_ep); + return ERR_PTR(-ENOMEM); + } + } + + sch_ep->sch_tt = tt; sch_ep->ep = ep; return sch_ep; @@ -128,6 +225,8 @@ static void setup_sch_info(struct usb_device *udev, CTX_TO_MAX_ESIT_PAYLOAD(le32_to_cpu(ep_ctx->tx_info)); sch_ep->esit = get_esit(ep_ctx); + sch_ep->ep_type = ep_type; + sch_ep->maxpkt = maxpkt; sch_ep->offset = 0; sch_ep->burst_mode = 0; sch_ep->repeat = 0; @@ -197,8 +296,13 @@ static void setup_sch_info(struct usb_device *udev, } } else if (is_fs_or_ls(udev->speed)) { sch_ep->pkts = 1; /* at most one packet for each microframe */ + + /* + * num_budget_microframes and cs_count will be updated when + * check TT for INT_OUT_EP, ISOC/INT_IN_EP type + */ sch_ep->cs_count = DIV_ROUND_UP(maxpkt, FS_PAYLOAD_MAX); - sch_ep->num_budget_microframes = sch_ep->cs_count + 2; + sch_ep->num_budget_microframes = sch_ep->cs_count; sch_ep->bw_cost_per_microframe = (maxpkt < FS_PAYLOAD_MAX) ? maxpkt : FS_PAYLOAD_MAX; @@ -212,7 +316,13 @@ static void setup_sch_info(struct usb_device *udev, } else { /* INT_IN_EP or ISOC_IN_EP */ bwb_table[0] = 0; /* start split */ bwb_table[1] = 0; /* idle */ - for (i = 2; i < sch_ep->num_budget_microframes; i++) + /* + * due to cs_count will be updated according to cs + * position, assign all remainder budget array + * elements as @bw_cost_per_microframe, but only first + * @num_budget_microframes elements will be used later + */ + for (i = 2; i < TT_MICROFRAMES_MAX; i++) bwb_table[i] = sch_ep->bw_cost_per_microframe; } } @@ -264,6 +374,96 @@ static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw, } } +static int check_sch_tt(struct usb_device *udev, + struct mu3h_sch_ep_info *sch_ep, u32 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; + + 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; + + /* + * usb_20 spec section11.18: + * 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; + + } else { + u32 cs_count = DIV_ROUND_UP(sch_ep->maxpkt, FS_PAYLOAD_MAX); + + /* + * usb_20 spec section11.18: + * must never schedule Start-Split in Y6 + */ + if (start_ss == 6) + return -ERANGE; + + /* 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; + + 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; + + 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; + } + + sch_ep->cs_count = cs_count; + /* one for ss, the other for idle */ + sch_ep->num_budget_microframes = cs_count + 2; + + /* + * if interval=1, maxp >752, num_budge_micoframe is larger + * than sch_ep->esit, will overstep boundary + */ + if (sch_ep->num_budget_microframes > sch_ep->esit) + sch_ep->num_budget_microframes = sch_ep->esit; + } + + return 0; +} + +static void update_sch_tt(struct usb_device *udev, + struct mu3h_sch_ep_info *sch_ep) +{ + struct mu3h_sch_tt *tt = sch_ep->sch_tt; + u32 base, num_esit; + int 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); + } + + list_add_tail(&sch_ep->tt_endpoint, &tt->ep_list); +} + static int check_sch_bw(struct usb_device *udev, struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep) { @@ -273,6 +473,10 @@ static int check_sch_bw(struct usb_device *udev, u32 min_index; u32 worst_bw; u32 bw_boundary; + u32 min_num_budget; + u32 min_cs_count; + bool tt_offset_ok = false; + int ret; esit = sch_ep->esit; @@ -282,26 +486,30 @@ static int check_sch_bw(struct usb_device *udev, */ min_bw = ~0; 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) + continue; + else + tt_offset_ok = true; + } + if ((offset + sch_ep->num_budget_microframes) > sch_ep->esit) break; - /* - * usb_20 spec section11.18: - * must never schedule Start-Split in Y6 - */ - if (is_fs_or_ls(udev->speed) && (offset % 8 == 6)) - continue; - worst_bw = get_max_bw(sch_bw, sch_ep, offset); if (min_bw > worst_bw) { min_bw = worst_bw; min_index = offset; + min_cs_count = sch_ep->cs_count; + min_num_budget = sch_ep->num_budget_microframes; } if (min_bw == 0) break; } - sch_ep->offset = min_index; bw_boundary = (udev->speed == USB_SPEED_SUPER) ? SS_BW_BOUNDARY : HS_BW_BOUNDARY; @@ -310,6 +518,18 @@ static int check_sch_bw(struct usb_device *udev, if (min_bw > bw_boundary) 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); @@ -415,6 +635,9 @@ int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev, ret = check_sch_bw(udev, sch_bw, sch_ep); if (ret) { xhci_err(xhci, "Not enough bandwidth!\n"); + if (is_fs_or_ls(udev->speed)) + drop_tt(udev); + kfree(sch_ep); return -ENOSPC; } @@ -466,6 +689,10 @@ void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev, if (sch_ep->ep == ep) { update_bus_bw(sch_bw, sch_ep, 0); list_del(&sch_ep->endpoint); + if (is_fs_or_ls(udev->speed)) { + list_del(&sch_ep->tt_endpoint); + drop_tt(udev); + } kfree(sch_ep); break; } diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h index f8864fc..8be8c5f 100644 --- a/drivers/usb/host/xhci-mtk.h +++ b/drivers/usb/host/xhci-mtk.h @@ -20,6 +20,19 @@ #define XHCI_MTK_MAX_ESIT 64 /** + * @split_bit_map: used to avoid split microframes overlay + * @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); + struct list_head ep_list; + struct usb_tt *usb_tt; + int tt_port; +}; + +/** * struct mu3h_sch_bw_info: schedule information for bandwidth domain * * @bus_bw: array to keep track of bandwidth already used at each uframes @@ -41,6 +54,10 @@ struct mu3h_sch_bw_info { * (@repeat==1) scheduled within the interval * @bw_cost_per_microframe: bandwidth cost per microframe * @endpoint: linked into bandwidth domain which it belongs to + * @tt_endpoint: linked into mu3h_sch_tt's list which it belongs to + * @sch_tt: mu3h_sch_tt linked into + * @ep_type: endpoint type + * @maxpkt: max packet size of endpoint * @ep: address of usb_host_endpoint struct * @offset: which uframe of the interval that transfer should be * scheduled first time within the interval @@ -64,6 +81,10 @@ struct mu3h_sch_ep_info { u32 num_budget_microframes; u32 bw_cost_per_microframe; struct list_head endpoint; + struct list_head tt_endpoint; + struct mu3h_sch_tt *sch_tt; + u32 ep_type; + u32 maxpkt; void *ep; /* * mtk xHCI scheduling information put into reserved DWs -- 1.9.1 From mboxrd@z Thu Jan 1 00:00:00 1970 From: Chunfeng Yun Subject: [PATCH 5/6] usb: xhci-mtk: supports bandwidth scheduling with multi-TT Date: Wed, 29 Aug 2018 10:55:17 +0800 Message-ID: <57f6f53d29d6adef7e12ed958ed29688fe37f7e0.1535510898.git.chunfeng.yun@mediatek.com> References: <3bcc220aa54bfebc39cea54cd736388ad37ee0c5.1535510898.git.chunfeng.yun@mediatek.com> Mime-Version: 1.0 Content-Type: text/plain Return-path: In-Reply-To: <3bcc220aa54bfebc39cea54cd736388ad37ee0c5.1535510898.git.chunfeng.yun@mediatek.com> Sender: linux-kernel-owner@vger.kernel.org To: Mathias Nyman Cc: Greg Kroah-Hartman , Felipe Balbi , Matthias Brugger , Alan Stern , Chunfeng Yun , linux-usb@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org List-Id: devicetree@vger.kernel.org Supports LowSpeed and FullSpeed INT/ISOC bandwidth scheduling with USB multi-TT Signed-off-by: Chunfeng Yun --- drivers/usb/host/xhci-mtk-sch.c | 247 ++++++++++++++++++++++++++++++++++++++-- drivers/usb/host/xhci-mtk.h | 21 ++++ 2 files changed, 258 insertions(+), 10 deletions(-) diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c index 7efd890..36050a1 100644 --- a/drivers/usb/host/xhci-mtk-sch.c +++ b/drivers/usb/host/xhci-mtk-sch.c @@ -80,10 +80,98 @@ static u32 get_esit(struct xhci_ep_ctx *ep_ctx) return esit; } +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) + return NULL; /* Not below a TT */ + + /* + * Find/create our data structure. + * For hubs with a single TT, we get it directly. + * For hubs with multiple TTs, there's an extra level of pointers. + */ + tt_index = NULL; + if (utt->multi) { + tt_index = utt->hcpriv; + if (!tt_index) { /* Create the index array */ + tt_index = kcalloc(utt->hub->maxchild, + sizeof(*tt_index), GFP_KERNEL); + if (!tt_index) + return ERR_PTR(-ENOMEM); + utt->hcpriv = tt_index; + allocated_index = true; + } + port = udev->ttport - 1; + ptt = &tt_index[port]; + } else { + port = 0; + ptt = (struct mu3h_sch_tt **) &utt->hcpriv; + } + + tt = *ptt; + if (!tt) { /* Create the mu3h_sch_tt */ + tt = kzalloc(sizeof(*tt), GFP_KERNEL); + if (!tt) { + if (allocated_index) { + utt->hcpriv = NULL; + kfree(tt_index); + } + return ERR_PTR(-ENOMEM); + } + INIT_LIST_HEAD(&tt->ep_list); + tt->usb_tt = utt; + tt->tt_port = port; + *ptt = tt; + } + + return tt; +} + +/* Release the TT above udev, if it's not in use */ +static void drop_tt(struct usb_device *udev) +{ + struct usb_tt *utt = udev->tt; + struct mu3h_sch_tt *tt, **tt_index, **ptt; + int i, cnt; + + if (!utt || !utt->hcpriv) + return; /* Not below a TT, or never allocated */ + + cnt = 0; + if (utt->multi) { + tt_index = utt->hcpriv; + ptt = &tt_index[udev->ttport - 1]; + /* How many entries are left in tt_index? */ + for (i = 0; i < utt->hub->maxchild; ++i) + cnt += !!tt_index[i]; + } else { + tt_index = NULL; + ptt = (struct mu3h_sch_tt **)&utt->hcpriv; + } + + tt = *ptt; + if (!tt || !list_empty(&tt->ep_list)) + return; /* never allocated , or still in use*/ + + *ptt = NULL; + kfree(tt); + + if (cnt == 1) { + utt->hcpriv = NULL; + kfree(tt_index); + } +} + 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; @@ -101,6 +189,15 @@ static struct mu3h_sch_ep_info *create_sch_ep(struct usb_device *udev, if (!sch_ep) return ERR_PTR(-ENOMEM); + if (is_fs_or_ls(udev->speed)) { + tt = find_tt(udev); + if (IS_ERR(tt)) { + kfree(sch_ep); + return ERR_PTR(-ENOMEM); + } + } + + sch_ep->sch_tt = tt; sch_ep->ep = ep; return sch_ep; @@ -128,6 +225,8 @@ static void setup_sch_info(struct usb_device *udev, CTX_TO_MAX_ESIT_PAYLOAD(le32_to_cpu(ep_ctx->tx_info)); sch_ep->esit = get_esit(ep_ctx); + sch_ep->ep_type = ep_type; + sch_ep->maxpkt = maxpkt; sch_ep->offset = 0; sch_ep->burst_mode = 0; sch_ep->repeat = 0; @@ -197,8 +296,13 @@ static void setup_sch_info(struct usb_device *udev, } } else if (is_fs_or_ls(udev->speed)) { sch_ep->pkts = 1; /* at most one packet for each microframe */ + + /* + * num_budget_microframes and cs_count will be updated when + * check TT for INT_OUT_EP, ISOC/INT_IN_EP type + */ sch_ep->cs_count = DIV_ROUND_UP(maxpkt, FS_PAYLOAD_MAX); - sch_ep->num_budget_microframes = sch_ep->cs_count + 2; + sch_ep->num_budget_microframes = sch_ep->cs_count; sch_ep->bw_cost_per_microframe = (maxpkt < FS_PAYLOAD_MAX) ? maxpkt : FS_PAYLOAD_MAX; @@ -212,7 +316,13 @@ static void setup_sch_info(struct usb_device *udev, } else { /* INT_IN_EP or ISOC_IN_EP */ bwb_table[0] = 0; /* start split */ bwb_table[1] = 0; /* idle */ - for (i = 2; i < sch_ep->num_budget_microframes; i++) + /* + * due to cs_count will be updated according to cs + * position, assign all remainder budget array + * elements as @bw_cost_per_microframe, but only first + * @num_budget_microframes elements will be used later + */ + for (i = 2; i < TT_MICROFRAMES_MAX; i++) bwb_table[i] = sch_ep->bw_cost_per_microframe; } } @@ -264,6 +374,96 @@ static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw, } } +static int check_sch_tt(struct usb_device *udev, + struct mu3h_sch_ep_info *sch_ep, u32 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; + + 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; + + /* + * usb_20 spec section11.18: + * 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; + + } else { + u32 cs_count = DIV_ROUND_UP(sch_ep->maxpkt, FS_PAYLOAD_MAX); + + /* + * usb_20 spec section11.18: + * must never schedule Start-Split in Y6 + */ + if (start_ss == 6) + return -ERANGE; + + /* 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; + + 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; + + 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; + } + + sch_ep->cs_count = cs_count; + /* one for ss, the other for idle */ + sch_ep->num_budget_microframes = cs_count + 2; + + /* + * if interval=1, maxp >752, num_budge_micoframe is larger + * than sch_ep->esit, will overstep boundary + */ + if (sch_ep->num_budget_microframes > sch_ep->esit) + sch_ep->num_budget_microframes = sch_ep->esit; + } + + return 0; +} + +static void update_sch_tt(struct usb_device *udev, + struct mu3h_sch_ep_info *sch_ep) +{ + struct mu3h_sch_tt *tt = sch_ep->sch_tt; + u32 base, num_esit; + int 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); + } + + list_add_tail(&sch_ep->tt_endpoint, &tt->ep_list); +} + static int check_sch_bw(struct usb_device *udev, struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep) { @@ -273,6 +473,10 @@ static int check_sch_bw(struct usb_device *udev, u32 min_index; u32 worst_bw; u32 bw_boundary; + u32 min_num_budget; + u32 min_cs_count; + bool tt_offset_ok = false; + int ret; esit = sch_ep->esit; @@ -282,26 +486,30 @@ static int check_sch_bw(struct usb_device *udev, */ min_bw = ~0; 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) + continue; + else + tt_offset_ok = true; + } + if ((offset + sch_ep->num_budget_microframes) > sch_ep->esit) break; - /* - * usb_20 spec section11.18: - * must never schedule Start-Split in Y6 - */ - if (is_fs_or_ls(udev->speed) && (offset % 8 == 6)) - continue; - worst_bw = get_max_bw(sch_bw, sch_ep, offset); if (min_bw > worst_bw) { min_bw = worst_bw; min_index = offset; + min_cs_count = sch_ep->cs_count; + min_num_budget = sch_ep->num_budget_microframes; } if (min_bw == 0) break; } - sch_ep->offset = min_index; bw_boundary = (udev->speed == USB_SPEED_SUPER) ? SS_BW_BOUNDARY : HS_BW_BOUNDARY; @@ -310,6 +518,18 @@ static int check_sch_bw(struct usb_device *udev, if (min_bw > bw_boundary) 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); @@ -415,6 +635,9 @@ int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev, ret = check_sch_bw(udev, sch_bw, sch_ep); if (ret) { xhci_err(xhci, "Not enough bandwidth!\n"); + if (is_fs_or_ls(udev->speed)) + drop_tt(udev); + kfree(sch_ep); return -ENOSPC; } @@ -466,6 +689,10 @@ void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev, if (sch_ep->ep == ep) { update_bus_bw(sch_bw, sch_ep, 0); list_del(&sch_ep->endpoint); + if (is_fs_or_ls(udev->speed)) { + list_del(&sch_ep->tt_endpoint); + drop_tt(udev); + } kfree(sch_ep); break; } diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h index f8864fc..8be8c5f 100644 --- a/drivers/usb/host/xhci-mtk.h +++ b/drivers/usb/host/xhci-mtk.h @@ -20,6 +20,19 @@ #define XHCI_MTK_MAX_ESIT 64 /** + * @split_bit_map: used to avoid split microframes overlay + * @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); + struct list_head ep_list; + struct usb_tt *usb_tt; + int tt_port; +}; + +/** * struct mu3h_sch_bw_info: schedule information for bandwidth domain * * @bus_bw: array to keep track of bandwidth already used at each uframes @@ -41,6 +54,10 @@ struct mu3h_sch_bw_info { * (@repeat==1) scheduled within the interval * @bw_cost_per_microframe: bandwidth cost per microframe * @endpoint: linked into bandwidth domain which it belongs to + * @tt_endpoint: linked into mu3h_sch_tt's list which it belongs to + * @sch_tt: mu3h_sch_tt linked into + * @ep_type: endpoint type + * @maxpkt: max packet size of endpoint * @ep: address of usb_host_endpoint struct * @offset: which uframe of the interval that transfer should be * scheduled first time within the interval @@ -64,6 +81,10 @@ struct mu3h_sch_ep_info { u32 num_budget_microframes; u32 bw_cost_per_microframe; struct list_head endpoint; + struct list_head tt_endpoint; + struct mu3h_sch_tt *sch_tt; + u32 ep_type; + u32 maxpkt; void *ep; /* * mtk xHCI scheduling information put into reserved DWs -- 1.9.1 From mboxrd@z Thu Jan 1 00:00:00 1970 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: base64 Subject: [5/6] usb: xhci-mtk: supports bandwidth scheduling with multi-TT From: Chunfeng Yun Message-Id: <57f6f53d29d6adef7e12ed958ed29688fe37f7e0.1535510898.git.chunfeng.yun@mediatek.com> Date: Wed, 29 Aug 2018 10:55:17 +0800 To: Mathias Nyman Cc: Greg Kroah-Hartman , Felipe Balbi , Matthias Brugger , Alan Stern , Chunfeng Yun , linux-usb@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org List-ID: U3VwcG9ydHMgTG93U3BlZWQgYW5kIEZ1bGxTcGVlZCBJTlQvSVNPQyBiYW5kd2lkdGggc2NoZWR1 bGluZwp3aXRoIFVTQiBtdWx0aS1UVAoKU2lnbmVkLW9mZi1ieTogQ2h1bmZlbmcgWXVuIDxjaHVu ZmVuZy55dW5AbWVkaWF0ZWsuY29tPgotLS0KIGRyaXZlcnMvdXNiL2hvc3QveGhjaS1tdGstc2No LmMgfCAyNDcgKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKystLQogZHJpdmVy cy91c2IvaG9zdC94aGNpLW10ay5oICAgICB8ICAyMSArKysrCiAyIGZpbGVzIGNoYW5nZWQsIDI1 OCBpbnNlcnRpb25zKCspLCAxMCBkZWxldGlvbnMoLSkKCmRpZmYgLS1naXQgYS9kcml2ZXJzL3Vz Yi9ob3N0L3hoY2ktbXRrLXNjaC5jIGIvZHJpdmVycy91c2IvaG9zdC94aGNpLW10ay1zY2guYwpp bmRleCA3ZWZkODkwLi4zNjA1MGExIDEwMDY0NAotLS0gYS9kcml2ZXJzL3VzYi9ob3N0L3hoY2kt bXRrLXNjaC5jCisrKyBiL2RyaXZlcnMvdXNiL2hvc3QveGhjaS1tdGstc2NoLmMKQEAgLTgwLDEw ICs4MCw5OCBAQCBzdGF0aWMgdTMyIGdldF9lc2l0KHN0cnVjdCB4aGNpX2VwX2N0eCAqZXBfY3R4 KQogCXJldHVybiBlc2l0OwogfQogCitzdGF0aWMgc3RydWN0IG11M2hfc2NoX3R0ICpmaW5kX3R0 KHN0cnVjdCB1c2JfZGV2aWNlICp1ZGV2KQoreworCXN0cnVjdCB1c2JfdHQgKnV0dCA9IHVkZXYt PnR0OworCXN0cnVjdCBtdTNoX3NjaF90dCAqdHQsICoqdHRfaW5kZXgsICoqcHR0OworCXVuc2ln bmVkIGludCBwb3J0OworCWJvb2wgYWxsb2NhdGVkX2luZGV4ID0gZmFsc2U7CisKKwlpZiAoIXV0 dCkKKwkJcmV0dXJuIE5VTEw7CS8qIE5vdCBiZWxvdyBhIFRUICovCisKKwkvKgorCSAqIEZpbmQv Y3JlYXRlIG91ciBkYXRhIHN0cnVjdHVyZS4KKwkgKiBGb3IgaHVicyB3aXRoIGEgc2luZ2xlIFRU LCB3ZSBnZXQgaXQgZGlyZWN0bHkuCisJICogRm9yIGh1YnMgd2l0aCBtdWx0aXBsZSBUVHMsIHRo ZXJlJ3MgYW4gZXh0cmEgbGV2ZWwgb2YgcG9pbnRlcnMuCisJICovCisJdHRfaW5kZXggPSBOVUxM OworCWlmICh1dHQtPm11bHRpKSB7CisJCXR0X2luZGV4ID0gdXR0LT5oY3ByaXY7CisJCWlmICgh dHRfaW5kZXgpIHsJLyogQ3JlYXRlIHRoZSBpbmRleCBhcnJheSAqLworCQkJdHRfaW5kZXggPSBr Y2FsbG9jKHV0dC0+aHViLT5tYXhjaGlsZCwKKwkJCQkJc2l6ZW9mKCp0dF9pbmRleCksIEdGUF9L RVJORUwpOworCQkJaWYgKCF0dF9pbmRleCkKKwkJCQlyZXR1cm4gRVJSX1BUUigtRU5PTUVNKTsK KwkJCXV0dC0+aGNwcml2ID0gdHRfaW5kZXg7CisJCQlhbGxvY2F0ZWRfaW5kZXggPSB0cnVlOwor CQl9CisJCXBvcnQgPSB1ZGV2LT50dHBvcnQgLSAxOworCQlwdHQgPSAmdHRfaW5kZXhbcG9ydF07 CisJfSBlbHNlIHsKKwkJcG9ydCA9IDA7CisJCXB0dCA9IChzdHJ1Y3QgbXUzaF9zY2hfdHQgKiop ICZ1dHQtPmhjcHJpdjsKKwl9CisKKwl0dCA9ICpwdHQ7CisJaWYgKCF0dCkgewkvKiBDcmVhdGUg dGhlIG11M2hfc2NoX3R0ICovCisJCXR0ID0ga3phbGxvYyhzaXplb2YoKnR0KSwgR0ZQX0tFUk5F TCk7CisJCWlmICghdHQpIHsKKwkJCWlmIChhbGxvY2F0ZWRfaW5kZXgpIHsKKwkJCQl1dHQtPmhj cHJpdiA9IE5VTEw7CisJCQkJa2ZyZWUodHRfaW5kZXgpOworCQkJfQorCQkJcmV0dXJuIEVSUl9Q VFIoLUVOT01FTSk7CisJCX0KKwkJSU5JVF9MSVNUX0hFQUQoJnR0LT5lcF9saXN0KTsKKwkJdHQt PnVzYl90dCA9IHV0dDsKKwkJdHQtPnR0X3BvcnQgPSBwb3J0OworCQkqcHR0ID0gdHQ7CisJfQor CisJcmV0dXJuIHR0OworfQorCisvKiBSZWxlYXNlIHRoZSBUVCBhYm92ZSB1ZGV2LCBpZiBpdCdz IG5vdCBpbiB1c2UgKi8KK3N0YXRpYyB2b2lkIGRyb3BfdHQoc3RydWN0IHVzYl9kZXZpY2UgKnVk ZXYpCit7CisJc3RydWN0IHVzYl90dCAqdXR0ID0gdWRldi0+dHQ7CisJc3RydWN0IG11M2hfc2No X3R0ICp0dCwgKip0dF9pbmRleCwgKipwdHQ7CisJaW50IGksIGNudDsKKworCWlmICghdXR0IHx8 ICF1dHQtPmhjcHJpdikKKwkJcmV0dXJuOwkJLyogTm90IGJlbG93IGEgVFQsIG9yIG5ldmVyIGFs bG9jYXRlZCAqLworCisJY250ID0gMDsKKwlpZiAodXR0LT5tdWx0aSkgeworCQl0dF9pbmRleCA9 IHV0dC0+aGNwcml2OworCQlwdHQgPSAmdHRfaW5kZXhbdWRldi0+dHRwb3J0IC0gMV07CisJCS8q ICBIb3cgbWFueSBlbnRyaWVzIGFyZSBsZWZ0IGluIHR0X2luZGV4PyAqLworCQlmb3IgKGkgPSAw OyBpIDwgdXR0LT5odWItPm1heGNoaWxkOyArK2kpCisJCQljbnQgKz0gISF0dF9pbmRleFtpXTsK Kwl9IGVsc2UgeworCQl0dF9pbmRleCA9IE5VTEw7CisJCXB0dCA9IChzdHJ1Y3QgbXUzaF9zY2hf dHQgKiopJnV0dC0+aGNwcml2OworCX0KKworCXR0ID0gKnB0dDsKKwlpZiAoIXR0IHx8ICFsaXN0 X2VtcHR5KCZ0dC0+ZXBfbGlzdCkpCisJCXJldHVybjsJCS8qIG5ldmVyIGFsbG9jYXRlZCAsIG9y IHN0aWxsIGluIHVzZSovCisKKwkqcHR0ID0gTlVMTDsKKwlrZnJlZSh0dCk7CisKKwlpZiAoY250 ID09IDEpIHsKKwkJdXR0LT5oY3ByaXYgPSBOVUxMOworCQlrZnJlZSh0dF9pbmRleCk7CisJfQor fQorCiBzdGF0aWMgc3RydWN0IG11M2hfc2NoX2VwX2luZm8gKmNyZWF0ZV9zY2hfZXAoc3RydWN0 IHVzYl9kZXZpY2UgKnVkZXYsCiAJc3RydWN0IHVzYl9ob3N0X2VuZHBvaW50ICplcCwgc3RydWN0 IHhoY2lfZXBfY3R4ICplcF9jdHgpCiB7CiAJc3RydWN0IG11M2hfc2NoX2VwX2luZm8gKnNjaF9l cDsKKwlzdHJ1Y3QgbXUzaF9zY2hfdHQgKnR0ID0gTlVMTDsKIAl1MzIgbGVuX2J3X2J1ZGdldF90 YWJsZTsKIAlzaXplX3QgbWVtX3NpemU7CiAKQEAgLTEwMSw2ICsxODksMTUgQEAgc3RhdGljIHN0 cnVjdCBtdTNoX3NjaF9lcF9pbmZvICpjcmVhdGVfc2NoX2VwKHN0cnVjdCB1c2JfZGV2aWNlICp1 ZGV2LAogCWlmICghc2NoX2VwKQogCQlyZXR1cm4gRVJSX1BUUigtRU5PTUVNKTsKIAorCWlmIChp c19mc19vcl9scyh1ZGV2LT5zcGVlZCkpIHsKKwkJdHQgPSBmaW5kX3R0KHVkZXYpOworCQlpZiAo SVNfRVJSKHR0KSkgeworCQkJa2ZyZWUoc2NoX2VwKTsKKwkJCXJldHVybiBFUlJfUFRSKC1FTk9N RU0pOworCQl9CisJfQorCisJc2NoX2VwLT5zY2hfdHQgPSB0dDsKIAlzY2hfZXAtPmVwID0gZXA7 CiAKIAlyZXR1cm4gc2NoX2VwOwpAQCAtMTI4LDYgKzIyNSw4IEBAIHN0YXRpYyB2b2lkIHNldHVw X3NjaF9pbmZvKHN0cnVjdCB1c2JfZGV2aWNlICp1ZGV2LAogCQkgQ1RYX1RPX01BWF9FU0lUX1BB WUxPQUQobGUzMl90b19jcHUoZXBfY3R4LT50eF9pbmZvKSk7CiAKIAlzY2hfZXAtPmVzaXQgPSBn ZXRfZXNpdChlcF9jdHgpOworCXNjaF9lcC0+ZXBfdHlwZSA9IGVwX3R5cGU7CisJc2NoX2VwLT5t YXhwa3QgPSBtYXhwa3Q7CiAJc2NoX2VwLT5vZmZzZXQgPSAwOwogCXNjaF9lcC0+YnVyc3RfbW9k ZSA9IDA7CiAJc2NoX2VwLT5yZXBlYXQgPSAwOwpAQCAtMTk3LDggKzI5NiwxMyBAQCBzdGF0aWMg dm9pZCBzZXR1cF9zY2hfaW5mbyhzdHJ1Y3QgdXNiX2RldmljZSAqdWRldiwKIAkJfQogCX0gZWxz ZSBpZiAoaXNfZnNfb3JfbHModWRldi0+c3BlZWQpKSB7CiAJCXNjaF9lcC0+cGt0cyA9IDE7IC8q IGF0IG1vc3Qgb25lIHBhY2tldCBmb3IgZWFjaCBtaWNyb2ZyYW1lICovCisKKwkJLyoKKwkJICog bnVtX2J1ZGdldF9taWNyb2ZyYW1lcyBhbmQgY3NfY291bnQgd2lsbCBiZSB1cGRhdGVkIHdoZW4K KwkJICogY2hlY2sgVFQgZm9yIElOVF9PVVRfRVAsIElTT0MvSU5UX0lOX0VQIHR5cGUKKwkJICov CiAJCXNjaF9lcC0+Y3NfY291bnQgPSBESVZfUk9VTkRfVVAobWF4cGt0LCBGU19QQVlMT0FEX01B WCk7Ci0JCXNjaF9lcC0+bnVtX2J1ZGdldF9taWNyb2ZyYW1lcyA9IHNjaF9lcC0+Y3NfY291bnQg KyAyOworCQlzY2hfZXAtPm51bV9idWRnZXRfbWljcm9mcmFtZXMgPSBzY2hfZXAtPmNzX2NvdW50 OwogCQlzY2hfZXAtPmJ3X2Nvc3RfcGVyX21pY3JvZnJhbWUgPQogCQkJKG1heHBrdCA8IEZTX1BB WUxPQURfTUFYKSA/IG1heHBrdCA6IEZTX1BBWUxPQURfTUFYOwogCkBAIC0yMTIsNyArMzE2LDEz IEBAIHN0YXRpYyB2b2lkIHNldHVwX3NjaF9pbmZvKHN0cnVjdCB1c2JfZGV2aWNlICp1ZGV2LAog CQl9IGVsc2UgeyAvKiBJTlRfSU5fRVAgb3IgSVNPQ19JTl9FUCAqLwogCQkJYndiX3RhYmxlWzBd ID0gMDsgLyogc3RhcnQgc3BsaXQgKi8KIAkJCWJ3Yl90YWJsZVsxXSA9IDA7IC8qIGlkbGUgKi8K LQkJCWZvciAoaSA9IDI7IGkgPCBzY2hfZXAtPm51bV9idWRnZXRfbWljcm9mcmFtZXM7IGkrKykK KwkJCS8qCisJCQkgKiBkdWUgdG8gY3NfY291bnQgd2lsbCBiZSB1cGRhdGVkIGFjY29yZGluZyB0 byBjcworCQkJICogcG9zaXRpb24sIGFzc2lnbiBhbGwgcmVtYWluZGVyIGJ1ZGdldCBhcnJheQor CQkJICogZWxlbWVudHMgYXMgQGJ3X2Nvc3RfcGVyX21pY3JvZnJhbWUsIGJ1dCBvbmx5IGZpcnN0 CisJCQkgKiBAbnVtX2J1ZGdldF9taWNyb2ZyYW1lcyBlbGVtZW50cyB3aWxsIGJlIHVzZWQgbGF0 ZXIKKwkJCSAqLworCQkJZm9yIChpID0gMjsgaSA8IFRUX01JQ1JPRlJBTUVTX01BWDsgaSsrKQog CQkJCWJ3Yl90YWJsZVtpXSA9CXNjaF9lcC0+YndfY29zdF9wZXJfbWljcm9mcmFtZTsKIAkJfQog CX0KQEAgLTI2NCw2ICszNzQsOTYgQEAgc3RhdGljIHZvaWQgdXBkYXRlX2J1c19idyhzdHJ1Y3Qg bXUzaF9zY2hfYndfaW5mbyAqc2NoX2J3LAogCX0KIH0KIAorc3RhdGljIGludCBjaGVja19zY2hf dHQoc3RydWN0IHVzYl9kZXZpY2UgKnVkZXYsCisJc3RydWN0IG11M2hfc2NoX2VwX2luZm8gKnNj aF9lcCwgdTMyIG9mZnNldCkKK3sKKwlzdHJ1Y3QgbXUzaF9zY2hfdHQgKnR0ID0gc2NoX2VwLT5z Y2hfdHQ7CisJdTMyIGV4dHJhX2NzX2NvdW50OworCXUzMiBmc19idWRnZXRfc3RhcnQ7CisJdTMy IHN0YXJ0X3NzLCBsYXN0X3NzOworCXUzMiBzdGFydF9jcywgbGFzdF9jczsKKwlpbnQgaTsKKwor CXN0YXJ0X3NzID0gb2Zmc2V0ICUgODsKKwlmc19idWRnZXRfc3RhcnQgPSAoc3RhcnRfc3MgKyAx KSAlIDg7CisKKwlpZiAoc2NoX2VwLT5lcF90eXBlID09IElTT0NfT1VUX0VQKSB7CisJCWxhc3Rf c3MgPSBzdGFydF9zcyArIHNjaF9lcC0+Y3NfY291bnQgLSAxOworCisJCS8qCisJCSAqIHVzYl8y MCBzcGVjIHNlY3Rpb24xMS4xODoKKwkJICogbXVzdCBuZXZlciBzY2hlZHVsZSBTdGFydC1TcGxp dCBpbiBZNgorCQkgKi8KKwkJaWYgKCEoc3RhcnRfc3MgPT0gNyB8fCBsYXN0X3NzIDwgNikpCisJ CQlyZXR1cm4gLUVSQU5HRTsKKworCQlmb3IgKGkgPSAwOyBpIDwgc2NoX2VwLT5jc19jb3VudDsg aSsrKQorCQkJaWYgKHRlc3RfYml0KG9mZnNldCArIGksIHR0LT5zcGxpdF9iaXRfbWFwKSkKKwkJ CQlyZXR1cm4gLUVSQU5HRTsKKworCX0gZWxzZSB7CisJCXUzMiBjc19jb3VudCA9IERJVl9ST1VO RF9VUChzY2hfZXAtPm1heHBrdCwgRlNfUEFZTE9BRF9NQVgpOworCisJCS8qCisJCSAqIHVzYl8y MCBzcGVjIHNlY3Rpb24xMS4xODoKKwkJICogbXVzdCBuZXZlciBzY2hlZHVsZSBTdGFydC1TcGxp dCBpbiBZNgorCQkgKi8KKwkJaWYgKHN0YXJ0X3NzID09IDYpCisJCQlyZXR1cm4gLUVSQU5HRTsK KworCQkvKiBvbmUgdWZyYW1lIGZvciBzcyArIG9uZSB1ZnJhbWUgZm9yIGlkbGUgKi8KKwkJc3Rh cnRfY3MgPSAoc3RhcnRfc3MgKyAyKSAlIDg7CisJCWxhc3RfY3MgPSBzdGFydF9jcyArIGNzX2Nv dW50IC0gMTsKKworCQlpZiAobGFzdF9jcyA+IDcpCisJCQlyZXR1cm4gLUVSQU5HRTsKKworCQlp ZiAoc2NoX2VwLT5lcF90eXBlID09IElTT0NfSU5fRVApCisJCQlleHRyYV9jc19jb3VudCA9IChs YXN0X2NzID09IDcpID8gMSA6IDI7CisJCWVsc2UgLyogIGVwX3R5cGUgOiBJTlRSIElOIC8gSU5U UiBPVVQgKi8KKwkJCWV4dHJhX2NzX2NvdW50ID0gKGZzX2J1ZGdldF9zdGFydCA9PSA2KSA/IDEg OiAyOworCisJCWNzX2NvdW50ICs9IGV4dHJhX2NzX2NvdW50OworCQlpZiAoY3NfY291bnQgPiA3 KQorCQkJY3NfY291bnQgPSA3OyAvKiBIVyBsaW1pdCAqLworCisJCWZvciAoaSA9IDA7IGkgPCBj c19jb3VudCArIDI7IGkrKykgeworCQkJaWYgKHRlc3RfYml0KG9mZnNldCArIGksIHR0LT5zcGxp dF9iaXRfbWFwKSkKKwkJCQlyZXR1cm4gLUVSQU5HRTsKKwkJfQorCisJCXNjaF9lcC0+Y3NfY291 bnQgPSBjc19jb3VudDsKKwkJLyogb25lIGZvciBzcywgdGhlIG90aGVyIGZvciBpZGxlICovCisJ CXNjaF9lcC0+bnVtX2J1ZGdldF9taWNyb2ZyYW1lcyA9IGNzX2NvdW50ICsgMjsKKworCQkvKgor CQkgKiBpZiBpbnRlcnZhbD0xLCBtYXhwID43NTIsIG51bV9idWRnZV9taWNvZnJhbWUgaXMgbGFy Z2VyCisJCSAqIHRoYW4gc2NoX2VwLT5lc2l0LCB3aWxsIG92ZXJzdGVwIGJvdW5kYXJ5CisJCSAq LworCQlpZiAoc2NoX2VwLT5udW1fYnVkZ2V0X21pY3JvZnJhbWVzID4gc2NoX2VwLT5lc2l0KQor CQkJc2NoX2VwLT5udW1fYnVkZ2V0X21pY3JvZnJhbWVzID0gc2NoX2VwLT5lc2l0OworCX0KKwor CXJldHVybiAwOworfQorCitzdGF0aWMgdm9pZCB1cGRhdGVfc2NoX3R0KHN0cnVjdCB1c2JfZGV2 aWNlICp1ZGV2LAorCXN0cnVjdCBtdTNoX3NjaF9lcF9pbmZvICpzY2hfZXApCit7CisJc3RydWN0 IG11M2hfc2NoX3R0ICp0dCA9IHNjaF9lcC0+c2NoX3R0OworCXUzMiBiYXNlLCBudW1fZXNpdDsK KwlpbnQgaSwgajsKKworCW51bV9lc2l0ID0gWEhDSV9NVEtfTUFYX0VTSVQgLyBzY2hfZXAtPmVz aXQ7CisJZm9yIChpID0gMDsgaSA8IG51bV9lc2l0OyBpKyspIHsKKwkJYmFzZSA9IHNjaF9lcC0+ b2Zmc2V0ICsgaSAqIHNjaF9lcC0+ZXNpdDsKKwkJZm9yIChqID0gMDsgaiA8IHNjaF9lcC0+bnVt X2J1ZGdldF9taWNyb2ZyYW1lczsgaisrKQorCQkJc2V0X2JpdChiYXNlICsgaiwgdHQtPnNwbGl0 X2JpdF9tYXApOworCX0KKworCWxpc3RfYWRkX3RhaWwoJnNjaF9lcC0+dHRfZW5kcG9pbnQsICZ0 dC0+ZXBfbGlzdCk7Cit9CisKIHN0YXRpYyBpbnQgY2hlY2tfc2NoX2J3KHN0cnVjdCB1c2JfZGV2 aWNlICp1ZGV2LAogCXN0cnVjdCBtdTNoX3NjaF9id19pbmZvICpzY2hfYncsIHN0cnVjdCBtdTNo X3NjaF9lcF9pbmZvICpzY2hfZXApCiB7CkBAIC0yNzMsNiArNDczLDEwIEBAIHN0YXRpYyBpbnQg Y2hlY2tfc2NoX2J3KHN0cnVjdCB1c2JfZGV2aWNlICp1ZGV2LAogCXUzMiBtaW5faW5kZXg7CiAJ dTMyIHdvcnN0X2J3OwogCXUzMiBid19ib3VuZGFyeTsKKwl1MzIgbWluX251bV9idWRnZXQ7CisJ dTMyIG1pbl9jc19jb3VudDsKKwlib29sIHR0X29mZnNldF9vayA9IGZhbHNlOworCWludCByZXQ7 CiAKIAllc2l0ID0gc2NoX2VwLT5lc2l0OwogCkBAIC0yODIsMjYgKzQ4NiwzMCBAQCBzdGF0aWMg aW50IGNoZWNrX3NjaF9idyhzdHJ1Y3QgdXNiX2RldmljZSAqdWRldiwKIAkgKi8KIAltaW5fYncg PSB+MDsKIAltaW5faW5kZXggPSAwOworCW1pbl9jc19jb3VudCA9IHNjaF9lcC0+Y3NfY291bnQ7 CisJbWluX251bV9idWRnZXQgPSBzY2hfZXAtPm51bV9idWRnZXRfbWljcm9mcmFtZXM7CiAJZm9y IChvZmZzZXQgPSAwOyBvZmZzZXQgPCBlc2l0OyBvZmZzZXQrKykgeworCQlpZiAoaXNfZnNfb3Jf bHModWRldi0+c3BlZWQpKSB7CisJCQlyZXQgPSBjaGVja19zY2hfdHQodWRldiwgc2NoX2VwLCBv ZmZzZXQpOworCQkJaWYgKHJldCkKKwkJCQljb250aW51ZTsKKwkJCWVsc2UKKwkJCQl0dF9vZmZz ZXRfb2sgPSB0cnVlOworCQl9CisKIAkJaWYgKChvZmZzZXQgKyBzY2hfZXAtPm51bV9idWRnZXRf bWljcm9mcmFtZXMpID4gc2NoX2VwLT5lc2l0KQogCQkJYnJlYWs7CiAKLQkJLyoKLQkJICogdXNi XzIwIHNwZWMgc2VjdGlvbjExLjE4OgotCQkgKiBtdXN0IG5ldmVyIHNjaGVkdWxlIFN0YXJ0LVNw bGl0IGluIFk2Ci0JCSAqLwotCQlpZiAoaXNfZnNfb3JfbHModWRldi0+c3BlZWQpICYmIChvZmZz ZXQgJSA4ID09IDYpKQotCQkJY29udGludWU7Ci0KIAkJd29yc3RfYncgPSBnZXRfbWF4X2J3KHNj aF9idywgc2NoX2VwLCBvZmZzZXQpOwogCQlpZiAobWluX2J3ID4gd29yc3RfYncpIHsKIAkJCW1p bl9idyA9IHdvcnN0X2J3OwogCQkJbWluX2luZGV4ID0gb2Zmc2V0OworCQkJbWluX2NzX2NvdW50 ID0gc2NoX2VwLT5jc19jb3VudDsKKwkJCW1pbl9udW1fYnVkZ2V0ID0gc2NoX2VwLT5udW1fYnVk Z2V0X21pY3JvZnJhbWVzOwogCQl9CiAJCWlmIChtaW5fYncgPT0gMCkKIAkJCWJyZWFrOwogCX0K LQlzY2hfZXAtPm9mZnNldCA9IG1pbl9pbmRleDsKIAogCWJ3X2JvdW5kYXJ5ID0gKHVkZXYtPnNw ZWVkID09IFVTQl9TUEVFRF9TVVBFUikKIAkJCQk/IFNTX0JXX0JPVU5EQVJZIDogSFNfQldfQk9V TkRBUlk7CkBAIC0zMTAsNiArNTE4LDE4IEBAIHN0YXRpYyBpbnQgY2hlY2tfc2NoX2J3KHN0cnVj dCB1c2JfZGV2aWNlICp1ZGV2LAogCWlmIChtaW5fYncgPiBid19ib3VuZGFyeSkKIAkJcmV0dXJu IC1FUkFOR0U7CiAKKwlzY2hfZXAtPm9mZnNldCA9IG1pbl9pbmRleDsKKwlzY2hfZXAtPmNzX2Nv dW50ID0gbWluX2NzX2NvdW50OworCXNjaF9lcC0+bnVtX2J1ZGdldF9taWNyb2ZyYW1lcyA9IG1p bl9udW1fYnVkZ2V0OworCisJaWYgKGlzX2ZzX29yX2xzKHVkZXYtPnNwZWVkKSkgeworCQkvKiBh bGwgb2Zmc2V0IGZvciB0dCBpcyBub3Qgb2sqLworCQlpZiAoIXR0X29mZnNldF9vaykKKwkJCXJl dHVybiAtRVJBTkdFOworCisJCXVwZGF0ZV9zY2hfdHQodWRldiwgc2NoX2VwKTsKKwl9CisKIAkv KiB1cGRhdGUgYnVzIGJhbmR3aWR0aCBpbmZvICovCiAJdXBkYXRlX2J1c19idyhzY2hfYncsIHNj aF9lcCwgMSk7CiAKQEAgLTQxNSw2ICs2MzUsOSBAQCBpbnQgeGhjaV9tdGtfYWRkX2VwX3F1aXJr KHN0cnVjdCB1c2JfaGNkICpoY2QsIHN0cnVjdCB1c2JfZGV2aWNlICp1ZGV2LAogCXJldCA9IGNo ZWNrX3NjaF9idyh1ZGV2LCBzY2hfYncsIHNjaF9lcCk7CiAJaWYgKHJldCkgewogCQl4aGNpX2Vy cih4aGNpLCAiTm90IGVub3VnaCBiYW5kd2lkdGghXG4iKTsKKwkJaWYgKGlzX2ZzX29yX2xzKHVk ZXYtPnNwZWVkKSkKKwkJCWRyb3BfdHQodWRldik7CisKIAkJa2ZyZWUoc2NoX2VwKTsKIAkJcmV0 dXJuIC1FTk9TUEM7CiAJfQpAQCAtNDY2LDYgKzY4OSwxMCBAQCB2b2lkIHhoY2lfbXRrX2Ryb3Bf ZXBfcXVpcmsoc3RydWN0IHVzYl9oY2QgKmhjZCwgc3RydWN0IHVzYl9kZXZpY2UgKnVkZXYsCiAJ CWlmIChzY2hfZXAtPmVwID09IGVwKSB7CiAJCQl1cGRhdGVfYnVzX2J3KHNjaF9idywgc2NoX2Vw LCAwKTsKIAkJCWxpc3RfZGVsKCZzY2hfZXAtPmVuZHBvaW50KTsKKwkJCWlmIChpc19mc19vcl9s cyh1ZGV2LT5zcGVlZCkpIHsKKwkJCQlsaXN0X2RlbCgmc2NoX2VwLT50dF9lbmRwb2ludCk7CisJ CQkJZHJvcF90dCh1ZGV2KTsKKwkJCX0KIAkJCWtmcmVlKHNjaF9lcCk7CiAJCQlicmVhazsKIAkJ fQpkaWZmIC0tZ2l0IGEvZHJpdmVycy91c2IvaG9zdC94aGNpLW10ay5oIGIvZHJpdmVycy91c2Iv aG9zdC94aGNpLW10ay5oCmluZGV4IGY4ODY0ZmMuLjhiZThjNWYgMTAwNjQ0Ci0tLSBhL2RyaXZl cnMvdXNiL2hvc3QveGhjaS1tdGsuaAorKysgYi9kcml2ZXJzL3VzYi9ob3N0L3hoY2ktbXRrLmgK QEAgLTIwLDYgKzIwLDE5IEBACiAjZGVmaW5lIFhIQ0lfTVRLX01BWF9FU0lUCTY0CiAKIC8qKgor ICogQHNwbGl0X2JpdF9tYXA6IHVzZWQgdG8gYXZvaWQgc3BsaXQgbWljcm9mcmFtZXMgb3Zlcmxh eQorICogQGVwX2xpc3Q6IEVuZHBvaW50cyB1c2luZyB0aGlzIFRUCisgKiBAdXNiX3R0OiB1c2Ig VFQgcmVsYXRlZAorICogQHR0X3BvcnQ6IFRUIHBvcnQgbnVtYmVyCisgKi8KK3N0cnVjdCBtdTNo X3NjaF90dCB7CisJREVDTEFSRV9CSVRNQVAoc3BsaXRfYml0X21hcCwgWEhDSV9NVEtfTUFYX0VT SVQpOworCXN0cnVjdCBsaXN0X2hlYWQgZXBfbGlzdDsKKwlzdHJ1Y3QgdXNiX3R0ICp1c2JfdHQ7 CisJaW50IHR0X3BvcnQ7Cit9OworCisvKioKICAqIHN0cnVjdCBtdTNoX3NjaF9id19pbmZvOiBz Y2hlZHVsZSBpbmZvcm1hdGlvbiBmb3IgYmFuZHdpZHRoIGRvbWFpbgogICoKICAqIEBidXNfYnc6 IGFycmF5IHRvIGtlZXAgdHJhY2sgb2YgYmFuZHdpZHRoIGFscmVhZHkgdXNlZCBhdCBlYWNoIHVm cmFtZXMKQEAgLTQxLDYgKzU0LDEwIEBAIHN0cnVjdCBtdTNoX3NjaF9id19pbmZvIHsKICAqCQko QHJlcGVhdD09MSkgc2NoZWR1bGVkIHdpdGhpbiB0aGUgaW50ZXJ2YWwKICAqIEBid19jb3N0X3Bl cl9taWNyb2ZyYW1lOiBiYW5kd2lkdGggY29zdCBwZXIgbWljcm9mcmFtZQogICogQGVuZHBvaW50 OiBsaW5rZWQgaW50byBiYW5kd2lkdGggZG9tYWluIHdoaWNoIGl0IGJlbG9uZ3MgdG8KKyAqIEB0 dF9lbmRwb2ludDogbGlua2VkIGludG8gbXUzaF9zY2hfdHQncyBsaXN0IHdoaWNoIGl0IGJlbG9u Z3MgdG8KKyAqIEBzY2hfdHQ6IG11M2hfc2NoX3R0IGxpbmtlZCBpbnRvCisgKiBAZXBfdHlwZTog ZW5kcG9pbnQgdHlwZQorICogQG1heHBrdDogbWF4IHBhY2tldCBzaXplIG9mIGVuZHBvaW50CiAg KiBAZXA6IGFkZHJlc3Mgb2YgdXNiX2hvc3RfZW5kcG9pbnQgc3RydWN0CiAgKiBAb2Zmc2V0OiB3 aGljaCB1ZnJhbWUgb2YgdGhlIGludGVydmFsIHRoYXQgdHJhbnNmZXIgc2hvdWxkIGJlCiAgKgkJ c2NoZWR1bGVkIGZpcnN0IHRpbWUgd2l0aGluIHRoZSBpbnRlcnZhbApAQCAtNjQsNiArODEsMTAg QEAgc3RydWN0IG11M2hfc2NoX2VwX2luZm8gewogCXUzMiBudW1fYnVkZ2V0X21pY3JvZnJhbWVz OwogCXUzMiBid19jb3N0X3Blcl9taWNyb2ZyYW1lOwogCXN0cnVjdCBsaXN0X2hlYWQgZW5kcG9p bnQ7CisJc3RydWN0IGxpc3RfaGVhZCB0dF9lbmRwb2ludDsKKwlzdHJ1Y3QgbXUzaF9zY2hfdHQg KnNjaF90dDsKKwl1MzIgZXBfdHlwZTsKKwl1MzIgbWF4cGt0OwogCXZvaWQgKmVwOwogCS8qCiAJ ICogbXRrIHhIQ0kgc2NoZWR1bGluZyBpbmZvcm1hdGlvbiBwdXQgaW50byByZXNlcnZlZCBEV3MK From mboxrd@z Thu Jan 1 00:00:00 1970 From: chunfeng.yun@mediatek.com (Chunfeng Yun) Date: Wed, 29 Aug 2018 10:55:17 +0800 Subject: [PATCH 5/6] usb: xhci-mtk: supports bandwidth scheduling with multi-TT In-Reply-To: <3bcc220aa54bfebc39cea54cd736388ad37ee0c5.1535510898.git.chunfeng.yun@mediatek.com> References: <3bcc220aa54bfebc39cea54cd736388ad37ee0c5.1535510898.git.chunfeng.yun@mediatek.com> Message-ID: <57f6f53d29d6adef7e12ed958ed29688fe37f7e0.1535510898.git.chunfeng.yun@mediatek.com> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org Supports LowSpeed and FullSpeed INT/ISOC bandwidth scheduling with USB multi-TT Signed-off-by: Chunfeng Yun --- drivers/usb/host/xhci-mtk-sch.c | 247 ++++++++++++++++++++++++++++++++++++++-- drivers/usb/host/xhci-mtk.h | 21 ++++ 2 files changed, 258 insertions(+), 10 deletions(-) diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c index 7efd890..36050a1 100644 --- a/drivers/usb/host/xhci-mtk-sch.c +++ b/drivers/usb/host/xhci-mtk-sch.c @@ -80,10 +80,98 @@ static u32 get_esit(struct xhci_ep_ctx *ep_ctx) return esit; } +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) + return NULL; /* Not below a TT */ + + /* + * Find/create our data structure. + * For hubs with a single TT, we get it directly. + * For hubs with multiple TTs, there's an extra level of pointers. + */ + tt_index = NULL; + if (utt->multi) { + tt_index = utt->hcpriv; + if (!tt_index) { /* Create the index array */ + tt_index = kcalloc(utt->hub->maxchild, + sizeof(*tt_index), GFP_KERNEL); + if (!tt_index) + return ERR_PTR(-ENOMEM); + utt->hcpriv = tt_index; + allocated_index = true; + } + port = udev->ttport - 1; + ptt = &tt_index[port]; + } else { + port = 0; + ptt = (struct mu3h_sch_tt **) &utt->hcpriv; + } + + tt = *ptt; + if (!tt) { /* Create the mu3h_sch_tt */ + tt = kzalloc(sizeof(*tt), GFP_KERNEL); + if (!tt) { + if (allocated_index) { + utt->hcpriv = NULL; + kfree(tt_index); + } + return ERR_PTR(-ENOMEM); + } + INIT_LIST_HEAD(&tt->ep_list); + tt->usb_tt = utt; + tt->tt_port = port; + *ptt = tt; + } + + return tt; +} + +/* Release the TT above udev, if it's not in use */ +static void drop_tt(struct usb_device *udev) +{ + struct usb_tt *utt = udev->tt; + struct mu3h_sch_tt *tt, **tt_index, **ptt; + int i, cnt; + + if (!utt || !utt->hcpriv) + return; /* Not below a TT, or never allocated */ + + cnt = 0; + if (utt->multi) { + tt_index = utt->hcpriv; + ptt = &tt_index[udev->ttport - 1]; + /* How many entries are left in tt_index? */ + for (i = 0; i < utt->hub->maxchild; ++i) + cnt += !!tt_index[i]; + } else { + tt_index = NULL; + ptt = (struct mu3h_sch_tt **)&utt->hcpriv; + } + + tt = *ptt; + if (!tt || !list_empty(&tt->ep_list)) + return; /* never allocated , or still in use*/ + + *ptt = NULL; + kfree(tt); + + if (cnt == 1) { + utt->hcpriv = NULL; + kfree(tt_index); + } +} + 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; @@ -101,6 +189,15 @@ static struct mu3h_sch_ep_info *create_sch_ep(struct usb_device *udev, if (!sch_ep) return ERR_PTR(-ENOMEM); + if (is_fs_or_ls(udev->speed)) { + tt = find_tt(udev); + if (IS_ERR(tt)) { + kfree(sch_ep); + return ERR_PTR(-ENOMEM); + } + } + + sch_ep->sch_tt = tt; sch_ep->ep = ep; return sch_ep; @@ -128,6 +225,8 @@ static void setup_sch_info(struct usb_device *udev, CTX_TO_MAX_ESIT_PAYLOAD(le32_to_cpu(ep_ctx->tx_info)); sch_ep->esit = get_esit(ep_ctx); + sch_ep->ep_type = ep_type; + sch_ep->maxpkt = maxpkt; sch_ep->offset = 0; sch_ep->burst_mode = 0; sch_ep->repeat = 0; @@ -197,8 +296,13 @@ static void setup_sch_info(struct usb_device *udev, } } else if (is_fs_or_ls(udev->speed)) { sch_ep->pkts = 1; /* at most one packet for each microframe */ + + /* + * num_budget_microframes and cs_count will be updated when + * check TT for INT_OUT_EP, ISOC/INT_IN_EP type + */ sch_ep->cs_count = DIV_ROUND_UP(maxpkt, FS_PAYLOAD_MAX); - sch_ep->num_budget_microframes = sch_ep->cs_count + 2; + sch_ep->num_budget_microframes = sch_ep->cs_count; sch_ep->bw_cost_per_microframe = (maxpkt < FS_PAYLOAD_MAX) ? maxpkt : FS_PAYLOAD_MAX; @@ -212,7 +316,13 @@ static void setup_sch_info(struct usb_device *udev, } else { /* INT_IN_EP or ISOC_IN_EP */ bwb_table[0] = 0; /* start split */ bwb_table[1] = 0; /* idle */ - for (i = 2; i < sch_ep->num_budget_microframes; i++) + /* + * due to cs_count will be updated according to cs + * position, assign all remainder budget array + * elements as @bw_cost_per_microframe, but only first + * @num_budget_microframes elements will be used later + */ + for (i = 2; i < TT_MICROFRAMES_MAX; i++) bwb_table[i] = sch_ep->bw_cost_per_microframe; } } @@ -264,6 +374,96 @@ static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw, } } +static int check_sch_tt(struct usb_device *udev, + struct mu3h_sch_ep_info *sch_ep, u32 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; + + 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; + + /* + * usb_20 spec section11.18: + * 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; + + } else { + u32 cs_count = DIV_ROUND_UP(sch_ep->maxpkt, FS_PAYLOAD_MAX); + + /* + * usb_20 spec section11.18: + * must never schedule Start-Split in Y6 + */ + if (start_ss == 6) + return -ERANGE; + + /* 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; + + 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; + + 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; + } + + sch_ep->cs_count = cs_count; + /* one for ss, the other for idle */ + sch_ep->num_budget_microframes = cs_count + 2; + + /* + * if interval=1, maxp >752, num_budge_micoframe is larger + * than sch_ep->esit, will overstep boundary + */ + if (sch_ep->num_budget_microframes > sch_ep->esit) + sch_ep->num_budget_microframes = sch_ep->esit; + } + + return 0; +} + +static void update_sch_tt(struct usb_device *udev, + struct mu3h_sch_ep_info *sch_ep) +{ + struct mu3h_sch_tt *tt = sch_ep->sch_tt; + u32 base, num_esit; + int 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); + } + + list_add_tail(&sch_ep->tt_endpoint, &tt->ep_list); +} + static int check_sch_bw(struct usb_device *udev, struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep) { @@ -273,6 +473,10 @@ static int check_sch_bw(struct usb_device *udev, u32 min_index; u32 worst_bw; u32 bw_boundary; + u32 min_num_budget; + u32 min_cs_count; + bool tt_offset_ok = false; + int ret; esit = sch_ep->esit; @@ -282,26 +486,30 @@ static int check_sch_bw(struct usb_device *udev, */ min_bw = ~0; 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) + continue; + else + tt_offset_ok = true; + } + if ((offset + sch_ep->num_budget_microframes) > sch_ep->esit) break; - /* - * usb_20 spec section11.18: - * must never schedule Start-Split in Y6 - */ - if (is_fs_or_ls(udev->speed) && (offset % 8 == 6)) - continue; - worst_bw = get_max_bw(sch_bw, sch_ep, offset); if (min_bw > worst_bw) { min_bw = worst_bw; min_index = offset; + min_cs_count = sch_ep->cs_count; + min_num_budget = sch_ep->num_budget_microframes; } if (min_bw == 0) break; } - sch_ep->offset = min_index; bw_boundary = (udev->speed == USB_SPEED_SUPER) ? SS_BW_BOUNDARY : HS_BW_BOUNDARY; @@ -310,6 +518,18 @@ static int check_sch_bw(struct usb_device *udev, if (min_bw > bw_boundary) 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); @@ -415,6 +635,9 @@ int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev, ret = check_sch_bw(udev, sch_bw, sch_ep); if (ret) { xhci_err(xhci, "Not enough bandwidth!\n"); + if (is_fs_or_ls(udev->speed)) + drop_tt(udev); + kfree(sch_ep); return -ENOSPC; } @@ -466,6 +689,10 @@ void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev, if (sch_ep->ep == ep) { update_bus_bw(sch_bw, sch_ep, 0); list_del(&sch_ep->endpoint); + if (is_fs_or_ls(udev->speed)) { + list_del(&sch_ep->tt_endpoint); + drop_tt(udev); + } kfree(sch_ep); break; } diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h index f8864fc..8be8c5f 100644 --- a/drivers/usb/host/xhci-mtk.h +++ b/drivers/usb/host/xhci-mtk.h @@ -20,6 +20,19 @@ #define XHCI_MTK_MAX_ESIT 64 /** + * @split_bit_map: used to avoid split microframes overlay + * @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); + struct list_head ep_list; + struct usb_tt *usb_tt; + int tt_port; +}; + +/** * struct mu3h_sch_bw_info: schedule information for bandwidth domain * * @bus_bw: array to keep track of bandwidth already used at each uframes @@ -41,6 +54,10 @@ struct mu3h_sch_bw_info { * (@repeat==1) scheduled within the interval * @bw_cost_per_microframe: bandwidth cost per microframe * @endpoint: linked into bandwidth domain which it belongs to + * @tt_endpoint: linked into mu3h_sch_tt's list which it belongs to + * @sch_tt: mu3h_sch_tt linked into + * @ep_type: endpoint type + * @maxpkt: max packet size of endpoint * @ep: address of usb_host_endpoint struct * @offset: which uframe of the interval that transfer should be * scheduled first time within the interval @@ -64,6 +81,10 @@ struct mu3h_sch_ep_info { u32 num_budget_microframes; u32 bw_cost_per_microframe; struct list_head endpoint; + struct list_head tt_endpoint; + struct mu3h_sch_tt *sch_tt; + u32 ep_type; + u32 maxpkt; void *ep; /* * mtk xHCI scheduling information put into reserved DWs -- 1.9.1