In current design, the ehci driver will not unlink itd/sitds from the hardware list when dequeue isochronous urbs. Rather just wait until they complete normally or their time slot expires. However, this will cause issues if the controller has stopped periodic schedule before finished all periodic schedule. The urb will not be done forever in this case and then usb_kill/poison_urb() will always wait there. The ChipIdea IP exactly has a bug: if frame babble occurs during periodic transfer, PE (PORTSC.bit2) will be cleared and the controller will stop periodic schedule immediately. So if the user tries to kill or poison related urb, it will wait there since the urb can't be done forever. This patch will check if this issue occurs, then it will unlink itd/sitds from the hardware list depends on the result. Signed-off-by: Xu Yang <xu.yang_2@xxxxxxx> --- drivers/usb/host/ehci-hcd.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index a1930db0da1c..26dc1d1ae5e8 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -930,10 +930,41 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { /* - * We don't expedite dequeue for isochronous URBs. + * 1. We don't expedite dequeue for isochronous URBs. * Just wait until they complete normally or their * time slot expires. + * + * 2. The ChipIdea IP has a bug: if frame babble occurs, + * PE will be cleared and the controller will stop periodic + * schedule. So if we don't force dequeue this urb, it + * won't be done forever. Here, a force dequeue is needed + * for this case. */ + unsigned i = HCS_N_PORTS (ehci->hcs_params); + bool need_force_dequeue = false; + + while (i--) { + int pstatus; + + pstatus = ehci_readl(ehci, + &ehci->regs->port_status[i]); + + /* Any cleared PE means controller has stopped + * periodic schedule. + */ + if (!(pstatus & PORT_PE)) { + need_force_dequeue = true; + break; + } + } + + if (!need_force_dequeue) + goto done; + + if (urb->dev->speed == USB_SPEED_HIGH) + itd_unlink_urb(ehci, urb); + else + sitd_unlink_urb(ehci, urb); } else { qh = (struct ehci_qh *) urb->hcpriv; qh->unlink_reason |= QH_UNLINK_REQUESTED; -- 2.34.1