Skip to content

Commit bea5892

Browse files
matnymangregkh
authored andcommitted
xhci: Limit time spent with xHC interrupts disabled during bus resume
Current xhci bus resume implementation prevents xHC host from generating interrupts during high-speed USB 2 and super-speed USB 3 bus resume. Only reason to disable interrupts during bus resume would be to prevent the interrupt handler from interfering with the resume process of USB 2 ports. Host initiated resume of USB 2 ports is done in two stages. The xhci driver first transitions the port from 'U3' to 'Resume' state, then wait in Resume for 20ms, and finally moves port to U0 state. xhci driver can't prevent interrupts by keeping the xhci spinlock due to this 20ms sleep. Limit interrupt disabling to the USB 2 port resume case only. resuming USB 2 ports in bus resume is only done in special cases where USB 2 ports had to be forced to suspend during bus suspend. The current way of preventing interrupts by clearing the 'Interrupt Enable' (INTE) bit in USBCMD register won't prevent the Interrupter registers 'Interrupt Pending' (IP), 'Event Handler Busy' (EHB) and USBSTS register Event Interrupt (EINT) bits from being set. New interrupts can't be issued before those bits are properly clered. Disable interrupts by clearing the interrupter register 'Interrupt Enable' (IE) bit instead. This way IP, EHB and INTE won't be set before IE is enabled again and a new interrupt is triggered. Reported-by: Devyn Liu <[email protected]> Closes: https://lore.kernel.org/linux-usb/[email protected]/ Cc: [email protected] Tested-by: Devyn Liu <[email protected]> Signed-off-by: Mathias Nyman <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 1ea050d commit bea5892

File tree

3 files changed

+20
-16
lines changed

3 files changed

+20
-16
lines changed

drivers/usb/host/xhci-hub.c

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1878,9 +1878,10 @@ int xhci_bus_resume(struct usb_hcd *hcd)
18781878
int max_ports, port_index;
18791879
int sret;
18801880
u32 next_state;
1881-
u32 temp, portsc;
1881+
u32 portsc;
18821882
struct xhci_hub *rhub;
18831883
struct xhci_port **ports;
1884+
bool disabled_irq = false;
18841885

18851886
rhub = xhci_get_rhub(hcd);
18861887
ports = rhub->ports;
@@ -1896,17 +1897,20 @@ int xhci_bus_resume(struct usb_hcd *hcd)
18961897
return -ESHUTDOWN;
18971898
}
18981899

1899-
/* delay the irqs */
1900-
temp = readl(&xhci->op_regs->command);
1901-
temp &= ~CMD_EIE;
1902-
writel(temp, &xhci->op_regs->command);
1903-
19041900
/* bus specific resume for ports we suspended at bus_suspend */
1905-
if (hcd->speed >= HCD_USB3)
1901+
if (hcd->speed >= HCD_USB3) {
19061902
next_state = XDEV_U0;
1907-
else
1903+
} else {
19081904
next_state = XDEV_RESUME;
1909-
1905+
if (bus_state->bus_suspended) {
1906+
/*
1907+
* prevent port event interrupts from interfering
1908+
* with usb2 port resume process
1909+
*/
1910+
xhci_disable_interrupter(xhci->interrupters[0]);
1911+
disabled_irq = true;
1912+
}
1913+
}
19101914
port_index = max_ports;
19111915
while (port_index--) {
19121916
portsc = readl(ports[port_index]->addr);
@@ -1974,11 +1978,9 @@ int xhci_bus_resume(struct usb_hcd *hcd)
19741978
(void) readl(&xhci->op_regs->command);
19751979

19761980
bus_state->next_statechange = jiffies + msecs_to_jiffies(5);
1977-
/* re-enable irqs */
1978-
temp = readl(&xhci->op_regs->command);
1979-
temp |= CMD_EIE;
1980-
writel(temp, &xhci->op_regs->command);
1981-
temp = readl(&xhci->op_regs->command);
1981+
/* re-enable interrupter */
1982+
if (disabled_irq)
1983+
xhci_enable_interrupter(xhci->interrupters[0]);
19821984

19831985
spin_unlock_irqrestore(&xhci->lock, flags);
19841986
return 0;

drivers/usb/host/xhci.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci)
322322
xhci_info(xhci, "Fault detected\n");
323323
}
324324

325-
static int xhci_enable_interrupter(struct xhci_interrupter *ir)
325+
int xhci_enable_interrupter(struct xhci_interrupter *ir)
326326
{
327327
u32 iman;
328328

@@ -335,7 +335,7 @@ static int xhci_enable_interrupter(struct xhci_interrupter *ir)
335335
return 0;
336336
}
337337

338-
static int xhci_disable_interrupter(struct xhci_interrupter *ir)
338+
int xhci_disable_interrupter(struct xhci_interrupter *ir)
339339
{
340340
u32 iman;
341341

drivers/usb/host/xhci.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,6 +1890,8 @@ int xhci_alloc_tt_info(struct xhci_hcd *xhci,
18901890
struct usb_tt *tt, gfp_t mem_flags);
18911891
int xhci_set_interrupter_moderation(struct xhci_interrupter *ir,
18921892
u32 imod_interval);
1893+
int xhci_enable_interrupter(struct xhci_interrupter *ir);
1894+
int xhci_disable_interrupter(struct xhci_interrupter *ir);
18931895

18941896
/* xHCI ring, segment, TRB, and TD functions */
18951897
dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb);

0 commit comments

Comments
 (0)