xhci: Workaround to get Intel xHCI reset working more reliably
Subsystems (3)
A message that is not in the archive (sent off-list or not yet received): [email protected]
PATCH 1/3 xhci: Workaround to get Intel xHCI reset working more reliably
From: Rajmohan Mani <[email protected]>
Existing Intel xHCI controllers require a delay of 1 mS, after setting the CMD_RESET bit in command register, before accessing any HC registers. This allows the HC to complete the reset operation and be ready for HC register access. Without this delay, the subsequent HC register access, may result in a system hang, very rarely.
Verified CherryView / Braswell platforms go through over 5000 warm reboot cycles (which was not possible without this patch), without any xHCI reset hang.
--- drivers/usb/host/xhci.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/usb/host/xhci.c +10 -0
@@ -175,6 +175,16 @@ int xhci_reset(struct xhci_hcd *xhci) command |=CMD_RESET ;writel (command, &xhci->op_regs->command); +/* Existing Intel xHCI controllers require a delay of 1 mS, +* after setting the CMD_RESET bit, and before accessing any +* HC registers. This allows the HC to complete the +* reset operation and be ready for HC register access. +* Without this delay, the subsequent HC register access, +* may result in a system hang very rarely. +*/ +if (xhci->quirks &XHCI_INTEL_HOST ) +udelay (1000 ); + ret =xhci_handshake (&xhci->op_regs->command,CMD_RESET ,0 ,10 *1000 *1000 );if (ret)
Signature · 1 line
1.9.1
PATCH 2/3 usb: xhci: fix checking ep busy for CFC
In reply to a message not in the archiveFrom: Lu Baolu <[email protected]>
Function ep_ring_is_processing() checks the dequeue pointer in endpoint context to know whether an endpoint is busy with processing TRBs. This is not correct since dequeue pointer field in an endpoint context is only valid when the endpoint is in Halted or Stopped states. This buggy code causes audio noise when playing sound with USB headset connected to host controllers which support CFC (one of xhci 1.1 features).
This patch should exist in stable kernel since v4.3.
--- drivers/usb/host/xhci-ring.c | 32 ++++++-------------------------- 1 file changed, 6 insertions(+), 26 deletions(-)
drivers/usb/host/xhci-ring.c +6 -26
@@ -3896,28 +3896,6 @@ cleanup:return ret; } -static int ep_ring_is_processing (struct xhci_hcd *xhci, -int slot_id,unsigned int ep_index) -{ -struct xhci_virt_device *xdev; -struct xhci_ring *ep_ring; -struct xhci_ep_ctx *ep_ctx; -struct xhci_virt_ep *xep; -dma_addr_t hw_deq; - - xdev = xhci->devs[slot_id]; - xep = &xhci->devs[slot_id]->eps[ep_index]; - ep_ring = xep->ring; - ep_ctx =xhci_get_ep_ctx (xhci, xdev->out_ctx, ep_index); - -if ((le32_to_cpu (ep_ctx->ep_info) &EP_STATE_MASK ) !=EP_STATE_RUNNING ) -return 0 ; - - hw_deq =le64_to_cpu (ep_ctx->deq) & ~EP_CTX_CYCLE_MASK ; -return (hw_deq != -xhci_trb_virt_to_dma (ep_ring->enq_seg, ep_ring->enqueue)); -} -/* * Check transfer ring to guarantee there is enough room for the urb. * Update ISO URB start_frame and interval.
@@ -3983,10 +3961,12 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, }/* Calculate the start frame and put it in urb->start_frame. */ -if (HCC_CFC (xhci->hcc_params) && -ep_ring_is_processing (xhci, slot_id, ep_index)) { - urb->start_frame = xep->next_frame_id; - goto skip_start_over; +if (HCC_CFC (xhci->hcc_params) && !list_empty (&ep_ring->td_list)) { +if ((le32_to_cpu (ep_ctx->ep_info) &EP_STATE_MASK ) == +EP_STATE_RUNNING ) { + urb->start_frame = xep->next_frame_id; + goto skip_start_over; + } } start_frame =readl (&xhci->run_regs->microframe_index);
Signature · 1 line
1.9.1
PATCH 3/3 xhci: Fix a race in usb2 LPM resume, blocking U3 for usb2 devices
In reply to a message not in the archiveClear device initiated resume variables once device is fully up and running in U0 state.
Resume needs to be signaled for 20ms for usb2 devices before they can be moved to U0 state.
An interrupt is triggered if a device initiates resume. As we handle the event in interrupt context we can not sleep for 20ms, so we instead set a resume flag, a timestamp, and start the roothub polling.
The roothub code will later move the port to U0 when it finds a port in resume state with the resume flag set, and timestamp passed by 20ms.
A host initiated resume is however not done in interrupt context, and host initiated resume code will directly signal resume, wait 20ms and then move the port to U0.
These two codepaths can race, if we are in the middle of a host initated resume, while sleeping for 20ms, we may handle a port event and find the port in resume state. The port event handling code will assume the resume was device initiated and set the resume flag and timestamp.
Root hub code will however not catch the port in resume state again as the host initated resume code has already moved the port to U0.
The resume flag and timestamp will remain set for this port preventing port from suspending again (LPM setting port to U3)
Fix this for now by always clearing the device initated resume parameters once port is in U0
--- drivers/usb/host/xhci-hub.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
drivers/usb/host/xhci-hub.c +9 -6
@@ -782,12 +782,15 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, status |=USB_PORT_STAT_SUSPEND ; } } -if ((raw_port_status &PORT_PLS_MASK ) ==XDEV_U0 - && (raw_port_status &PORT_POWER ) - && (bus_state->suspended_ports & (1 << wIndex))) { - bus_state->suspended_ports &= ~(1 << wIndex); -if (hcd->speed <HCD_USB3 ) - bus_state->port_c_suspend |=1 << wIndex; +if ((raw_port_status &PORT_PLS_MASK ) ==XDEV_U0 && + (raw_port_status &PORT_POWER )) { +if (bus_state->suspended_ports & (1 << wIndex)) { + bus_state->suspended_ports &= ~(1 << wIndex); +if (hcd->speed <HCD_USB3 ) + bus_state->port_c_suspend |=1 << wIndex; + } + bus_state->resume_done[wIndex] =0 ; +clear_bit (wIndex, &bus_state->resuming_ports); }if (raw_port_status &PORT_CONNECT ) { status |=USB_PORT_STAT_CONNECTION ;
Signature · 1 line
1.9.1