summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMathias Nyman <mathias.nyman@linux.intel.com>2024-02-16 16:09:36 -0800
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2024-02-17 16:24:38 +0100
commit9affb1d9a9d9918adff519a129daba5e369dd741 (patch)
treefba77b772b619be3e700ef515853b5735f5d434a
parentedc47759bbc7bf5902676bac441c195a4c7e4180 (diff)
downloadlinux-stable-9affb1d9a9d9918adff519a129daba5e369dd741.tar.gz
linux-stable-9affb1d9a9d9918adff519a129daba5e369dd741.tar.bz2
linux-stable-9affb1d9a9d9918adff519a129daba5e369dd741.zip
xhci: add helper to stop endpoint and wait for completion
Expose xhci_stop_endpoint_sync() which is a synchronous variant of xhci_queue_stop_endpoint(). This is useful for client drivers that are using the secondary interrupters, and need to stop/clean up the current session. The stop endpoint command handler will also take care of cleaning up the ring. Modifications to repurpose the new API into existing stop endpoint sequences was implemented by Wesley Cheng. Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Co-developed-by: Wesley Cheng <quic_wcheng@quicinc.com> Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com> Link: https://lore.kernel.org/r/20240217001017.29969-11-quic_wcheng@quicinc.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/host/xhci.c62
-rw-r--r--drivers/usb/host/xhci.h2
2 files changed, 48 insertions, 16 deletions
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index b405b8236134..872ce8c1a5ba 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -2796,6 +2796,48 @@ static int xhci_reserve_bandwidth(struct xhci_hcd *xhci,
return -ENOMEM;
}
+/*
+ * Synchronous XHCI stop endpoint helper. Issues the stop endpoint command and
+ * waits for the command completion before returning.
+ */
+int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, int suspend,
+ gfp_t gfp_flags)
+{
+ struct xhci_command *command;
+ unsigned long flags;
+ int ret;
+
+ command = xhci_alloc_command(xhci, true, gfp_flags);
+ if (!command)
+ return -ENOMEM;
+
+ spin_lock_irqsave(&xhci->lock, flags);
+ ret = xhci_queue_stop_endpoint(xhci, command, ep->vdev->slot_id,
+ ep->ep_index, suspend);
+ if (ret < 0) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ goto out;
+ }
+
+ xhci_ring_cmd_db(xhci);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
+ ret = wait_for_completion_timeout(command->completion, msecs_to_jiffies(3000));
+ if (!ret)
+ xhci_warn(xhci, "%s: Unable to stop endpoint.\n",
+ __func__);
+
+ if (command->status == COMP_COMMAND_ABORTED ||
+ command->status == COMP_COMMAND_RING_STOPPED) {
+ xhci_warn(xhci, "Timeout while waiting for stop endpoint command\n");
+ ret = -ETIME;
+ }
+out:
+ xhci_free_command(xhci, command);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(xhci_stop_endpoint_sync);
/* Issue a configure endpoint command or evaluate context command
* and wait for it to finish.
@@ -3119,7 +3161,7 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd,
struct xhci_virt_device *vdev;
struct xhci_virt_ep *ep;
struct xhci_input_control_ctx *ctrl_ctx;
- struct xhci_command *stop_cmd, *cfg_cmd;
+ struct xhci_command *cfg_cmd;
unsigned int ep_index;
unsigned long flags;
u32 ep_flag;
@@ -3177,10 +3219,6 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd,
if (ep_flag == SLOT_FLAG || ep_flag == EP0_FLAG)
return;
- stop_cmd = xhci_alloc_command(xhci, true, GFP_NOWAIT);
- if (!stop_cmd)
- return;
-
cfg_cmd = xhci_alloc_command_with_ctx(xhci, true, GFP_NOWAIT);
if (!cfg_cmd)
goto cleanup;
@@ -3203,23 +3241,16 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd,
goto cleanup;
}
- err = xhci_queue_stop_endpoint(xhci, stop_cmd, udev->slot_id,
- ep_index, 0);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
+ err = xhci_stop_endpoint_sync(xhci, ep, 0, GFP_NOWAIT);
if (err < 0) {
- spin_unlock_irqrestore(&xhci->lock, flags);
- xhci_free_command(xhci, cfg_cmd);
xhci_dbg(xhci, "%s: Failed to queue stop ep command, %d ",
__func__, err);
goto cleanup;
}
- xhci_ring_cmd_db(xhci);
- spin_unlock_irqrestore(&xhci->lock, flags);
-
- wait_for_completion(stop_cmd->completion);
-
spin_lock_irqsave(&xhci->lock, flags);
-
/* config ep command clears toggle if add and drop ep flags are set */
ctrl_ctx = xhci_get_input_control_ctx(cfg_cmd->in_ctx);
if (!ctrl_ctx) {
@@ -3251,7 +3282,6 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd,
xhci_free_command(xhci, cfg_cmd);
cleanup:
- xhci_free_command(xhci, stop_cmd);
spin_lock_irqsave(&xhci->lock, flags);
if (ep->ep_state & EP_SOFT_CLEAR_TOGGLE)
ep->ep_state &= ~EP_SOFT_CLEAR_TOGGLE;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 6e09b9130fae..4f5e5187ea46 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1920,6 +1920,8 @@ void xhci_ring_doorbell_for_active_rings(struct xhci_hcd *xhci,
void xhci_cleanup_command_queue(struct xhci_hcd *xhci);
void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring);
unsigned int count_trbs(u64 addr, u64 len);
+int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
+ int suspend, gfp_t gfp_flags);
/* xHCI roothub code */
void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,