From patchwork Fri Apr 9 01:42:06 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thinh Nguyen X-Patchwork-Id: 418587 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=-15.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED 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 23350C433ED for ; Fri, 9 Apr 2021 01:42:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id ED49A6113C for ; Fri, 9 Apr 2021 01:42:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232996AbhDIBmU (ORCPT ); Thu, 8 Apr 2021 21:42:20 -0400 Received: from smtprelay-out1.synopsys.com ([149.117.73.133]:45240 "EHLO smtprelay-out1.synopsys.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232426AbhDIBmT (ORCPT ); Thu, 8 Apr 2021 21:42:19 -0400 Received: from mailhost.synopsys.com (sv1-mailhost1.synopsys.com [10.205.2.131]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by smtprelay-out1.synopsys.com (Postfix) with ESMTPS id E938F40460; Fri, 9 Apr 2021 01:42:07 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=synopsys.com; s=mail; t=1617932528; bh=MG/ebsovEtVc0OlCOkauFe9m7MED2ttT8tNJWSSeoiQ=; h=Date:In-Reply-To:References:From:Subject:To:Cc:From; b=NP5Q9IHTVGAFB1o4uJb7szbgwEBnl5+suc5GQC3PN08xRqxq9RI5yTbJvqQyoTG1f jPzbRc6uyQyUhHR2cbzq4iOw5rjShp3So7ZWLxQ1O7J9RGosdhdZ8I6StaVwmJYX/6 sUT8aCKk/XQdP72+hVz6IYhH/GuSsHm2f27vygzN4aKnJogThqbsyyfjL55CWcFrHG RywfX85AARmF22XTbn5cLPmnzRnmw7f65zVYU9aWkP/7+zvVL7D/nbzjFOI6qSe/HI kImBALTs7YyUJE3lQTpRs4V9uRwe934SUlDro2MH4xM3A9yvrFGeNaVoj5I+ji5yct QNNUxH3K/Hxag== Received: from lab-vbox (unknown [10.205.144.97]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mailhost.synopsys.com (Postfix) with ESMTPSA id 89AF5A006A; Fri, 9 Apr 2021 01:42:06 +0000 (UTC) Received: by lab-vbox (sSMTP sendmail emulation); Thu, 08 Apr 2021 18:42:06 -0700 Date: Thu, 08 Apr 2021 18:42:06 -0700 Message-Id: <2064c68f7ccea6eb550499dad02acc9ee3c979d4.1617929509.git.Thinh.Nguyen@synopsys.com> In-Reply-To: References: X-SNPS-Relay: synopsys.com From: Thinh Nguyen Subject: [PATCH 2/6] usb: xhci: Check for blocked disconnection To: Felipe Balbi , Greg Kroah-Hartman , Thinh.Nguyen@synopsys.com, linux-usb@vger.kernel.org, Mathias Nyman Cc: John Youn Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org If there is a device with active enhanced super-speed (eSS) isoc IN endpoint(s) behind one or more eSS hubs, DWC_usb31 (v1.90a and prior) host controller will not detect the device disconnection until no more isoc URB is submitted. If there's a device disconnection, internally the wait for tHostTransactionTimeout (USB 3.2 spec 8.13) blocks the other endpoints from being scheduled. So, it blocks the interrupt endpoint of the eSS hub indicating the port change status. This can be an issue for applications that continuously submitting isoc URBs to the xHCI. To work around this, stop processing new URBs after 3 consecutive isoc transaction errors. If new isoc transfers are queued after the device is disconnected, the host will respond with USB transaction error. After 3 consecutive USB transaction errors, the driver can wait a period of time (at least 2 * largest periodic interval of the topology) without ringing isoc endpoint doorbell to detect the port change status. If there is no disconnection detected, ring the endpoint doorbell to resume isoc transfers. This workaround tracks the max eSS periodic interval every time there's an endpoint added or dropped, which happens when there's bandwidth check. So, scan the topology and update the xhci->max_ess_interval whenever there's a bandwidth check. Introduced a new flag VDEV_DISCONN_CHECK_PENDING to prevent ringing the doorbell while waiting for a disconnection status. After 2 * max_ess_interval time and no disconnection detected, a delayed work will ring the doorbell to resume the active isoc transfers. Signed-off-by: Thinh Nguyen --- drivers/usb/host/xhci-mem.c | 3 ++ drivers/usb/host/xhci-ring.c | 76 +++++++++++++++++++++++++++++++++ drivers/usb/host/xhci.c | 49 +++++++++++++++++++++ drivers/usb/host/xhci.h | 10 +++++ include/linux/usb/xhci-quirks.h | 1 + 5 files changed, 139 insertions(+) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index f66815fe8482..1053b43008e4 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -995,6 +995,8 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, xhci_dbg(xhci, "Slot %d input ctx = 0x%llx (dma)\n", slot_id, (unsigned long long)dev->in_ctx->dma); + INIT_DELAYED_WORK(&dev->resume_isoc, xhci_resume_isoc); + /* Initialize the cancellation list and watchdog timers for each ep */ for (i = 0; i < 31; i++) { dev->eps[i].ep_index = i; @@ -1010,6 +1012,7 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, goto fail; dev->udev = udev; + dev->xhci = xhci; /* Point to output device context in dcbaa. */ xhci->dcbaa->dev_context_ptrs[slot_id] = cpu_to_le64(dev->out_ctx->dma); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 05c38dd3ee36..a434a4b3609f 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -419,6 +419,14 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index]; unsigned int ep_state = ep->ep_state; + /* + * Don't ring the doorbell for isoc IN endpoint while checking for + * device disconnection. + */ + if (ep->ring && ep->ring->type == TYPE_ISOC && !(ep_index % 2) && + (xhci->devs[slot_id]->flags & VDEV_DISCONN_CHECK_PENDING)) + return; + /* Don't ring the doorbell for this endpoint if there are pending * cancellations because we don't want to interrupt processing. * We don't want to restart any stream rings if there's a set dequeue @@ -2330,6 +2338,8 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, struct xhci_ring *ep_ring, struct xhci_td *td, union xhci_trb *ep_trb, struct xhci_transfer_event *event) { + struct usb_device *udev; + struct xhci_virt_device *vdev; struct urb_priv *urb_priv; int idx; struct usb_iso_packet_descriptor *frame; @@ -2347,10 +2357,13 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, ep_trb_len = TRB_LEN(le32_to_cpu(ep_trb->generic.field[2])); short_framestatus = td->urb->transfer_flags & URB_SHORT_NOT_OK ? -EREMOTEIO : 0; + udev = td->urb->dev; + vdev = xhci->devs[udev->slot_id]; /* handle completion code */ switch (trb_comp_code) { case COMP_SUCCESS: + ep->ring->err_count = 0; if (remaining) { frame->status = short_framestatus; if (xhci->quirks & XHCI_TRUST_TX_LENGTH) @@ -2375,6 +2388,23 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, frame->status = -EPROTO; break; case COMP_USB_TRANSACTION_ERROR: + if ((xhci->quirks & XHCI_ISOC_BLOCKED_DISCONNECT) && + usb_urb_dir_in(td->urb) && + udev->parent && udev->parent->parent && + udev->speed >= USB_SPEED_SUPER) { + if (!(vdev->flags & VDEV_DISCONN_CHECK_PENDING) && + ep->ring->err_count++ >= 3) { + unsigned long timeout; + + /* Wait for at least max interval x 2 x 125us */ + timeout = (1 << xhci->max_ess_interval) * 250; + vdev->flags |= VDEV_DISCONN_CHECK_PENDING; + queue_delayed_work(system_wq, + &vdev->resume_isoc, + usecs_to_jiffies(timeout)); + } + } + frame->status = -EPROTO; if (ep_trb != td->last_trb) return 0; @@ -4171,6 +4201,9 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, for (i = 0; i < num_tds; i++) num_trbs += count_isoc_trbs_needed(urb, i); + if ((xdev->flags & VDEV_DISCONN_CHECK_PENDING) && usb_urb_dir_in(urb)) + return -EINVAL; + /* Check the ring to guarantee there is enough room for the whole urb. * Do not insert any td of the urb to the ring if the check failed. */ @@ -4359,3 +4392,46 @@ int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd, return queue_command(xhci, cmd, 0, 0, 0, trb_slot_id | trb_ep_index | type, false); } + +void xhci_resume_isoc(struct work_struct *work) +{ + struct xhci_hcd *xhci; + struct xhci_virt_device *vdev; + unsigned int slot_id; + unsigned long flags; + + vdev = container_of(to_delayed_work(work), + struct xhci_virt_device, resume_isoc); + xhci = vdev->xhci; + + spin_lock_irqsave(&xhci->lock, flags); + + /* Check if the device is dropped before this work takes place */ + if (!vdev->udev) + goto out; + + slot_id = vdev->udev->slot_id; + + vdev->flags &= ~VDEV_DISCONN_CHECK_PENDING; + + /* Resume isoc transfers if the device is still connected */ + if (xhci->devs[slot_id]) { + int i; + + /* Ring doorbell for IN isoc endpoints only */ + for (i = 2; i < 31; i += 2) { + struct xhci_virt_ep *ep = &vdev->eps[i]; + + if (!ep) + break; + + if (ep->ring && ep->ring->type == TYPE_ISOC) { + ep->ring->err_count = 0; + ring_doorbell_for_active_rings(xhci, slot_id, i); + } + } + } + +out: + spin_unlock_irqrestore(&xhci->lock, flags); +} diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index ca9385d22f68..e1136a6b9372 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -2973,6 +2973,44 @@ static void xhci_check_bw_drop_ep_streams(struct xhci_hcd *xhci, } } +static void xhci_update_ess_max_interval(struct xhci_hcd *xhci) +{ + unsigned int max_ess_interval = 0; + int j; + + for (j = 1; j < HCS_MAX_SLOTS(xhci->hcs_params1); j++) { + struct xhci_virt_device *virt_dev; + int i; + + virt_dev = xhci->devs[j]; + if (!virt_dev) + continue; + + /* Only update for eSS devices */ + if (virt_dev->udev && + virt_dev->udev->speed < USB_SPEED_SUPER) + continue; + + for (i = 0; i < 31; i++) { + struct xhci_ep_ctx *ep_ctx; + unsigned int ep_type; + unsigned int interval; + + ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->out_ctx, i); + ep_type = CTX_TO_EP_TYPE(le32_to_cpu(ep_ctx->ep_info2)); + + if (xhci_is_async_ep(ep_type)) + continue; + + interval = CTX_TO_EP_INTERVAL(le32_to_cpu(ep_ctx->ep_info)); + if (interval > max_ess_interval) + max_ess_interval = interval; + } + } + + xhci->max_ess_interval = max_ess_interval; +} + /* Called after one or more calls to xhci_add_endpoint() or * xhci_drop_endpoint(). If this call fails, the USB core is expected * to call xhci_reset_bandwidth(). @@ -3047,6 +3085,17 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) /* Callee should call reset_bandwidth() */ goto command_cleanup; + if (xhci->quirks & XHCI_ISOC_BLOCKED_DISCONNECT) { + xhci_update_ess_max_interval(xhci); + + /* Cancel disconnection check on change of context */ + if (delayed_work_pending(&virt_dev->resume_isoc) && + ctrl_ctx->drop_flags) { + cancel_delayed_work(&virt_dev->resume_isoc); + virt_dev->flags &= ~VDEV_DISCONN_CHECK_PENDING; + } + } + /* Free any rings that were dropped, but not changed. */ for (i = 1; i < 31; i++) { if ((le32_to_cpu(ctrl_ctx->drop_flags) & (1 << (i + 1))) && diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 9a4e2808668b..27d2c1176dd1 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1000,6 +1000,7 @@ struct xhci_interval_bw_table { struct xhci_virt_device { int slot_id; + struct xhci_hcd *xhci; struct usb_device *udev; /* * Commands to the hardware are passed an "input context" that @@ -1025,11 +1026,15 @@ struct xhci_virt_device { */ unsigned long flags; #define VDEV_PORT_ERROR BIT(0) /* Port error, link inactive */ +#define VDEV_DISCONN_CHECK_PENDING BIT(1) /* Disconnection check */ /* The current max exit latency for the enabled USB3 link states. */ u16 current_mel; /* Used for the debugfs interfaces. */ void *debugfs_private; + + /* For undetected disconnection quirk */ + struct delayed_work resume_isoc; }; /* @@ -1864,6 +1869,9 @@ struct xhci_hcd { /* Compliance Mode Timer Triggered every 2 seconds */ #define COMP_MODE_RCVRY_MSECS 2000 + /* Track max eSS interval for XHCI_ISOC_BLOCKED_DISCONNECT */ + unsigned int max_ess_interval; + struct dentry *debugfs_root; struct dentry *debugfs_slots; struct list_head regset_list; @@ -1948,6 +1956,8 @@ char *xhci_get_slot_state(struct xhci_hcd *xhci, void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *), const char *fmt, ...); +void xhci_resume_isoc(struct work_struct *work); + /* xHCI memory management */ void xhci_mem_cleanup(struct xhci_hcd *xhci); int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags); diff --git a/include/linux/usb/xhci-quirks.h b/include/linux/usb/xhci-quirks.h index c2cb35c5b273..c78638ba4735 100644 --- a/include/linux/usb/xhci-quirks.h +++ b/include/linux/usb/xhci-quirks.h @@ -58,6 +58,7 @@ #define XHCI_DISABLE_SPARSE BIT_ULL(38) #define XHCI_SG_TRB_CACHE_SIZE_QUIRK BIT_ULL(39) #define XHCI_NO_SOFT_RETRY BIT_ULL(40) +#define XHCI_ISOC_BLOCKED_DISCONNECT BIT_ULL(41) struct usb_hcd; From patchwork Fri Apr 9 01:42:19 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thinh Nguyen X-Patchwork-Id: 418586 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=-15.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED 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 85473C433ED for ; Fri, 9 Apr 2021 01:42:22 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 60D2A60190 for ; Fri, 9 Apr 2021 01:42:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233200AbhDIBmd (ORCPT ); Thu, 8 Apr 2021 21:42:33 -0400 Received: from smtprelay-out1.synopsys.com ([149.117.87.133]:47588 "EHLO smtprelay-out1.synopsys.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233153AbhDIBmc (ORCPT ); Thu, 8 Apr 2021 21:42:32 -0400 Received: from mailhost.synopsys.com (sv2-mailhost2.synopsys.com [10.205.2.134]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by smtprelay-out1.synopsys.com (Postfix) with ESMTPS id C6E6CC00CA; Fri, 9 Apr 2021 01:42:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=synopsys.com; s=mail; t=1617932540; bh=+frHFzkavFcIw/rtKwSdenkc0gQz6Etpz0ryVfSa+mo=; h=Date:In-Reply-To:References:From:Subject:To:Cc:From; b=FLqnMElTyp4PYJB/vV+x3JFh5Upiqzv+wDe3NTam7JOxnGXaGVek+h40WfbRA0VMp rlO2uszVZHH/gZ8uN9r756gdYtrDsLNPMU97T/xVDJAMbWcn16ySeCwemEYZNmJDOE CTFa4QojGcg6TtZlQhzo6tLGwtSXq0aX4V0GBa7h8573G5WBvl/GyH5EI/JcH02NAa Yv1WetxmcyKBCm80HKU1u25+/KCHkrusvSbt3UlfoEtY0TWD2O4bYrAGLFzdNlQdHy 5gwP4S5a8a/zoyXZO0P4eiKof8VQNyU1i+Xb5lsIv215gJpnTgz5O6SvOHOizlxeoN c8zzDDfAq7jBg== Received: from lab-vbox (unknown [10.205.144.97]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mailhost.synopsys.com (Postfix) with ESMTPSA id 8156BA0096; Fri, 9 Apr 2021 01:42:19 +0000 (UTC) Received: by lab-vbox (sSMTP sendmail emulation); Thu, 08 Apr 2021 18:42:19 -0700 Date: Thu, 08 Apr 2021 18:42:19 -0700 Message-Id: In-Reply-To: References: X-SNPS-Relay: synopsys.com From: Thinh Nguyen Subject: [PATCH 4/6] usb: xhci: Rename Compliance mode timer quirk To: Felipe Balbi , Greg Kroah-Hartman , Thinh.Nguyen@synopsys.com, linux-usb@vger.kernel.org, Mathias Nyman Cc: John Youn Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org In preparation for a workaround that needs to poll for the port status, rename the timer for XHCI_COMP_MODE_QUIRK to be more generic as it can be used for the new workaround. No funtional change here. Signed-off-by: Thinh Nguyen --- drivers/usb/host/xhci-hub.c | 2 +- drivers/usb/host/xhci.c | 41 +++++++++++++++++-------------------- drivers/usb/host/xhci.h | 8 ++++---- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index e9b18fc17617..8bfafbd680ab 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -893,7 +893,7 @@ static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, if ((xhci->port_status_u0 != all_ports_seen_u0) && port_in_u0) { xhci->port_status_u0 |= 1 << wIndex; if (xhci->port_status_u0 == all_ports_seen_u0) { - del_timer_sync(&xhci->comp_mode_recovery_timer); + del_timer_sync(&xhci->port_check_timer); xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, "All USB3 ports have entered U0 already!"); xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index e1136a6b9372..e1b3d1063f6b 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -475,7 +475,7 @@ static inline void xhci_msix_sync_irqs(struct xhci_hcd *xhci) #endif -static void compliance_mode_recovery(struct timer_list *t) +static void port_check(struct timer_list *t) { struct xhci_hcd *xhci; struct usb_hcd *hcd; @@ -483,7 +483,7 @@ static void compliance_mode_recovery(struct timer_list *t) u32 temp; int i; - xhci = from_timer(xhci, t, comp_mode_recovery_timer); + xhci = from_timer(xhci, t, port_check_timer); rhub = &xhci->usb3_rhub; for (i = 0; i < rhub->num_ports; i++) { @@ -508,8 +508,8 @@ static void compliance_mode_recovery(struct timer_list *t) } if (xhci->port_status_u0 != ((1 << rhub->num_ports) - 1)) - mod_timer(&xhci->comp_mode_recovery_timer, - jiffies + msecs_to_jiffies(COMP_MODE_RCVRY_MSECS)); + mod_timer(&xhci->port_check_timer, + jiffies + msecs_to_jiffies(PORT_CHECK_MSECS)); } /* @@ -522,15 +522,14 @@ static void compliance_mode_recovery(struct timer_list *t) * status event is generated when entering compliance mode (per xhci spec), * this quirk is needed on systems that have the failing hardware installed. */ -static void compliance_mode_recovery_timer_init(struct xhci_hcd *xhci) +static void port_check_timer_init(struct xhci_hcd *xhci) { xhci->port_status_u0 = 0; - timer_setup(&xhci->comp_mode_recovery_timer, compliance_mode_recovery, - 0); - xhci->comp_mode_recovery_timer.expires = jiffies + - msecs_to_jiffies(COMP_MODE_RCVRY_MSECS); + timer_setup(&xhci->port_check_timer, port_check, 0); + xhci->port_check_timer.expires = jiffies + + msecs_to_jiffies(PORT_CHECK_MSECS); - add_timer(&xhci->comp_mode_recovery_timer); + add_timer(&xhci->port_check_timer); xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, "Compliance mode recovery timer initialized"); } @@ -596,7 +595,7 @@ static int xhci_init(struct usb_hcd *hcd) /* Initializing Compliance Mode Recovery Data If Needed */ if (xhci_compliance_mode_recovery_timer_quirk_check()) { xhci->quirks |= XHCI_COMP_MODE_QUIRK; - compliance_mode_recovery_timer_init(xhci); + port_check_timer_init(xhci); } return retval; @@ -739,10 +738,9 @@ static void xhci_stop(struct usb_hcd *hcd) /* Deleting Compliance Mode Recovery Timer */ if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && (!(xhci_all_ports_seen_u0(xhci)))) { - del_timer_sync(&xhci->comp_mode_recovery_timer); + del_timer_sync(&xhci->port_check_timer); xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, - "%s: compliance mode recovery timer deleted", - __func__); + "%s: port check timer deleted", __func__); } if (xhci->quirks & XHCI_AMD_PLL_FIX) @@ -1057,15 +1055,14 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) spin_unlock_irq(&xhci->lock); /* - * Deleting Compliance Mode Recovery Timer because the xHCI Host + * Deleting Port Check Timer because the xHCI Host * is about to be suspended. */ if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && (!(xhci_all_ports_seen_u0(xhci)))) { - del_timer_sync(&xhci->comp_mode_recovery_timer); + del_timer_sync(&xhci->port_check_timer); xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, - "%s: compliance mode recovery timer deleted", - __func__); + "%s: port check timer deleted", __func__); } /* step 5: remove core well power */ @@ -1150,9 +1147,9 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && !(xhci_all_ports_seen_u0(xhci))) { - del_timer_sync(&xhci->comp_mode_recovery_timer); + del_timer_sync(&xhci->port_check_timer); xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, - "Compliance Mode Recovery Timer deleted!"); + "Port Check Timer deleted!"); } /* Let the USB core know _both_ roothubs lost power. */ @@ -1245,13 +1242,13 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) } } /* - * If system is subject to the Quirk, Compliance Mode Timer needs to + * If system is subject to the Quirk, Port Check Timer needs to * be re-initialized Always after a system resume. Ports are subject * to suffer the Compliance Mode issue again. It doesn't matter if * ports have entered previously to U0 before system's suspension. */ if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && !comp_timer_running) - compliance_mode_recovery_timer_init(xhci); + port_check_timer_init(xhci); if (xhci->quirks & XHCI_ASMEDIA_MODIFY_FLOWCONTROL) usb_asmedia_modifyflowcontrol(to_pci_dev(hcd->self.controller)); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 27d2c1176dd1..b52b7dcb5bb9 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1862,12 +1862,12 @@ struct xhci_hcd { /* cached extended protocol port capabilities */ struct xhci_port_cap *port_caps; unsigned int num_port_caps; - /* Compliance Mode Recovery Data */ - struct timer_list comp_mode_recovery_timer; + /* For quirks that require to poll for port status */ + struct timer_list port_check_timer; u32 port_status_u0; u16 test_mode; -/* Compliance Mode Timer Triggered every 2 seconds */ -#define COMP_MODE_RCVRY_MSECS 2000 +/* Port polling frequency */ +#define PORT_CHECK_MSECS 2000 /* Track max eSS interval for XHCI_ISOC_BLOCKED_DISCONNECT */ unsigned int max_ess_interval; From patchwork Fri Apr 9 01:42:32 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thinh Nguyen X-Patchwork-Id: 418585 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=-15.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED 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 56867C433B4 for ; Fri, 9 Apr 2021 01:42:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 31CAD610F7 for ; Fri, 9 Apr 2021 01:42:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233158AbhDIBmr (ORCPT ); Thu, 8 Apr 2021 21:42:47 -0400 Received: from smtprelay-out1.synopsys.com ([149.117.87.133]:47602 "EHLO smtprelay-out1.synopsys.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232426AbhDIBmq (ORCPT ); Thu, 8 Apr 2021 21:42:46 -0400 Received: from mailhost.synopsys.com (sv1-mailhost1.synopsys.com [10.205.2.131]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by smtprelay-out1.synopsys.com (Postfix) with ESMTPS id 21C6AC00CA; Fri, 9 Apr 2021 01:42:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=synopsys.com; s=mail; t=1617932554; bh=DIMCHZSRXXjfYtacNGDpPEqi4k9O2sDIegxiRCrb64I=; h=Date:In-Reply-To:References:From:Subject:To:Cc:From; b=jCWuVXOWuFYuqxdeVkmb6XgzPM+32qbhBwmOe/+CDcPKqn50c/9X72hD+A0HVNx79 QWHBxUc6QvwwLY/tW1BEEVtaX8UHPE8zSQLZKJAjk8MRUIU3MS6RJykFrbu5ikHKp7 lR+/bitT9XxScBWDMh6iVcdUOTbV/v6AYChANUdStDGpGVXnYGccckCx06ibIUK2kk QmyTQj2gUIJtyLV/S+rb1YNLh7cQK4FSPrKTZJH+LjvuvHrfvk1gdujCN3l7OT9qUp aBNl1D10e8JiLfccEwx9o64FwxHXK6qzeISHvq3kl9TRxplxYRERif+c1B+6mY1rKK pXBOhqDwOB5SA== Received: from lab-vbox (unknown [10.205.144.97]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mailhost.synopsys.com (Postfix) with ESMTPSA id D340CA006A; Fri, 9 Apr 2021 01:42:32 +0000 (UTC) Received: by lab-vbox (sSMTP sendmail emulation); Thu, 08 Apr 2021 18:42:32 -0700 Date: Thu, 08 Apr 2021 18:42:32 -0700 Message-Id: In-Reply-To: References: X-SNPS-Relay: synopsys.com From: Thinh Nguyen Subject: [PATCH 6/6] usb: dwc3: host: Set quirks base on version To: Felipe Balbi , Greg Kroah-Hartman , Thinh.Nguyen@synopsys.com, linux-usb@vger.kernel.org Cc: John Youn Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org We can check for host quirks at runtime base on the controller IP and version check. Set the following quirks for the DWC_usb31 IP host mode before creating a platform device for the xHCI driver: * XHCI_ISOC_BLOCKED_DISCONNECT * XHCI_LIMIT_FS_BI_INTR_EP * XHCI_LOST_DISCONNECT_QUIRK Signed-off-by: Thinh Nguyen --- drivers/usb/dwc3/host.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index f29a264635aa..a486d7fbb163 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -9,6 +9,7 @@ #include #include +#include #include "core.h" @@ -42,6 +43,17 @@ static int dwc3_host_get_irq(struct dwc3 *dwc) return irq; } +static void dwc3_host_init_quirks(struct dwc3 *dwc, struct xhci_plat_priv *priv) +{ + memset(priv, 0, sizeof(*priv)); + + if (DWC3_VER_IS_WITHIN(DWC31, ANY, 190A)) { + priv->quirks |= XHCI_ISOC_BLOCKED_DISCONNECT; + priv->quirks |= XHCI_LIMIT_FS_BI_INTR_EP; + priv->quirks |= XHCI_LOST_DISCONNECT_QUIRK; + } +} + int dwc3_host_init(struct dwc3 *dwc) { struct property_entry props[4]; @@ -49,6 +61,7 @@ int dwc3_host_init(struct dwc3 *dwc) int ret, irq; struct resource *res; struct platform_device *dwc3_pdev = to_platform_device(dwc->dev); + struct xhci_plat_priv dwc3_priv; int prop_idx = 0; irq = dwc3_host_get_irq(dwc); @@ -87,6 +100,14 @@ int dwc3_host_init(struct dwc3 *dwc) goto err; } + dwc3_host_init_quirks(dwc, &dwc3_priv); + + ret = platform_device_add_data(xhci, &dwc3_priv, sizeof(dwc3_priv)); + if (ret) { + dev_err(dwc->dev, "couldn't add platform data to xHCI device\n"); + goto err; + } + memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props)); if (dwc->usb3_lpm_capable)