From patchwork Mon Feb 8 03:27:16 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?b?Q2h1bmZlbmcgWXVuICjkupHmmKXls7Ap?= X-Patchwork-Id: 12073793 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=-17.3 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,UNPARSEABLE_RELAY, URIBL_BLOCKED,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 E288FC433DB for ; Mon, 8 Feb 2021 03:28:02 +0000 (UTC) Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 7632D64E24 for ; Mon, 8 Feb 2021 03:28:02 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 7632D64E24 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=mediatek.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-mediatek-bounces+linux-mediatek=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=merlin.20170209; h=Sender:Content-Transfer-Encoding: Content-Type:Cc:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:Message-ID:Date:Subject:To:From: Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender :Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Owner; bh=x7+gEGPmnwEzwYdV9NvSx3R80wc4gxjhacnnV9iH59c=; b=zwb+vBQZ2XyRhqXhsZhkpZYTUL THbncIU994XR1Uh7ZUq5h9hwXRc3VUXDJ7MB8lo/o1d2KJI+RZ9rg09u//BD8Sox5HPbZbLUyg6zW +KTxY3fOjUvogVQm7487dNdFjjPzjX87dI2oI7x7I5w/miwvUS2ToKChNFwCZdkAQkBgVD6F7Wizj tqm/Iyyt1O5+zUwEyz3MpSoN1ySTKkPdoSTw0Jf5K0+9R1IINjHORrmKh+mBRIbChaPq28XTvhLQB FMGMA4D2ErDRlzRGmn2mManWKauB/BBVwg4ALPBXK22og2YwLoBDVfejHVzh55cM1xHGE7cRAlLna Oh2aqC4w==; Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1l8xDI-0004ME-28; Mon, 08 Feb 2021 03:27:48 +0000 Received: from mailgw02.mediatek.com ([216.200.240.185]) by merlin.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1l8xDF-0004LM-2B for linux-mediatek@lists.infradead.org; Mon, 08 Feb 2021 03:27:47 +0000 X-UUID: e07b5395f12f43918d9ca51c7b6f7ec1-20210207 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mediatek.com; s=dk; h=Content-Transfer-Encoding:Content-Type:MIME-Version:Message-ID:Date:Subject:CC:To:From; bh=otDZcf8p5zDO/nHfZI+s+R78B9JMRJ14nEAg8JiJRcE=; b=gUzj1Ad+Wfvcl2MZT8kWt/f0cuToATkTE/qR2T7VkWGyJ7Pk4YeEFG1CCEhQkOtqTug5rZVDaEfU8dJAt0QorX6ECMASjOPUanOuM3tQRuAx7VZ7G6KR0s5+UOLumS9jibyjOxxZa4iI0Ct/zrwfen8etd24m0xoRu91QWW+I7I=; X-UUID: e07b5395f12f43918d9ca51c7b6f7ec1-20210207 Received: from mtkcas66.mediatek.inc [(172.29.193.44)] by mailgw02.mediatek.com (envelope-from ) (musrelay.mediatek.com ESMTP with TLSv1.2 ECDHE-RSA-AES256-SHA384 256/256) with ESMTP id 1578183266; Sun, 07 Feb 2021 19:27:35 -0800 Received: from MTKMBS31N1.mediatek.inc (172.27.4.69) by MTKMBS62DR.mediatek.inc (172.29.94.18) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Sun, 7 Feb 2021 19:27:34 -0800 Received: from mtkcas10.mediatek.inc (172.21.101.39) by MTKMBS31N1.mediatek.inc (172.27.4.69) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Mon, 8 Feb 2021 11:27:25 +0800 Received: from mtkslt301.mediatek.inc (10.21.14.114) by mtkcas10.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Mon, 8 Feb 2021 11:27:24 +0800 From: Chunfeng Yun To: Ikjoon Jang Subject: [RFC v4 PATCH] usb: xhci-mtk: improve bandwidth scheduling with TT Date: Mon, 8 Feb 2021 11:27:16 +0800 Message-ID: <20210208032716.20247-1-chunfeng.yun@mediatek.com> X-Mailer: git-send-email 2.18.0 MIME-Version: 1.0 X-TM-SNTS-SMTP: DACF80511D283FC03702EF5940D89001F2A8650EB8CB6C6433F2CBAD121F1A972000:8 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210207_222745_980884_775985D5 X-CRM114-Status: GOOD ( 20.48 ) X-BeenThere: linux-mediatek@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Chunfeng Yun , Yaqii Wu , linux-mediatek@lists.infradead.org, linux-kernel@vger.kernel.org, Tianping Fang Sender: "Linux-mediatek" Errors-To: linux-mediatek-bounces+linux-mediatek=archiver.kernel.org@lists.infradead.org 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 Signed-off-by: Chunfeng Yun Tested-by: Ikjoon Jang --- 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