summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Stanley <joel@jms.id.au>2017-04-07 17:57:00 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-04-08 13:17:41 +0300
commitfe190ed0d60260e44f48d8b0b04f26a8c8898a02 (patch)
tree769428594a5c00884ac842913f29e4b011e6e59d
parentb7f769ae1b126086c5ec6686734924bac1dc0a9f (diff)
downloadlinux-fe190ed0d60260e44f48d8b0b04f26a8c8898a02.tar.xz
xhci: Do not halt the host until both HCD have disconnected their devices.
We can't halt the host controller immediately when first HCD is removed as it will cause problems if we have devices attached to the second (primary) HCD, like a keyboard. We've been carrying this in our Linux-as-a-bootloader environment for a little while now. The machines all have the same TI TUSB73x0 part, and when we kexec the devices don't come back until a system power cycle. [minor adjustments, code comments and remove HALT check -Mathias] Signed-off-by: Joel Stanley <joel@jms.id.au> Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/host/xhci.c20
1 files changed, 10 insertions, 10 deletions
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index e523dbd1ee4e..5ecba7c85889 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -692,21 +692,21 @@ void xhci_stop(struct usb_hcd *hcd)
mutex_lock(&xhci->mutex);
- if (!(xhci->xhc_state & XHCI_STATE_HALTED)) {
- spin_lock_irq(&xhci->lock);
-
- xhci->xhc_state |= XHCI_STATE_HALTED;
- xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
- xhci_halt(xhci);
- xhci_reset(xhci);
- spin_unlock_irq(&xhci->lock);
- }
-
+ /* Only halt host and free memory after both hcds are removed */
if (!usb_hcd_is_primary_hcd(hcd)) {
+ /* usb core will free this hcd shortly, unset pointer */
+ xhci->shared_hcd = NULL;
mutex_unlock(&xhci->mutex);
return;
}
+ spin_lock_irq(&xhci->lock);
+ xhci->xhc_state |= XHCI_STATE_HALTED;
+ xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
+ xhci_halt(xhci);
+ xhci_reset(xhci);
+ spin_unlock_irq(&xhci->lock);
+
xhci_cleanup_msix(xhci);
/* Deleting Compliance Mode Recovery Timer */