USB: OHCI handles more ZFMicro quirks
The ZF Micro OHCI controller exhibits unexpected behavior that seems to be related to high load. Under certain conditions, the controller will complete a TD, remove it from the endpoint's queue, and fail to add it to the donelist. This causes the endpoint to appear to stop responding. Worse, if the device is removed while in that state, OHCI will hang while waiting for the orphaned TD to complete. The situation is not recoverable without rebooting. This fix enhances the scope of the existing OHCI_QUIRK_ZFMICRO flag: 1. A watchdog routine periodically scans the OHCI structures to check for orphaned TDs. In these cases the TD is taken back from the controller and completed normally. 2. If a device is removed while the endpoint is hung but before the watchdog catches the situation, any outstanding TDs are taken back from the controller in the 'sanitize' phase. The ohci-hcd driver used to print "INTR_SF lossage" in this situation; this changes it to the universally accurate "ED unlink timeout". Other instances of this message presumably have different root causes. Both this Compaq quirk and a NEC quirk are now properly compiled out for non-PCI builds of this driver. Signed-off-by: Mike Nuss <mike@terascala.com> Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
e8fa0ce65c
commit
89a0fd18a9
@@ -84,7 +84,7 @@ static int ohci_quirk_zfmicro(struct usb_hcd *hcd)
|
||||
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
|
||||
|
||||
ohci->flags |= OHCI_QUIRK_ZFMICRO;
|
||||
ohci_dbg (ohci, "enabled Compaq ZFMicro chipset quirk\n");
|
||||
ohci_dbg(ohci, "enabled Compaq ZFMicro chipset quirks\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -113,11 +113,31 @@ static int ohci_quirk_toshiba_scc(struct usb_hcd *hcd)
|
||||
|
||||
/* Check for NEC chip and apply quirk for allegedly lost interrupts.
|
||||
*/
|
||||
|
||||
static void ohci_quirk_nec_worker(struct work_struct *work)
|
||||
{
|
||||
struct ohci_hcd *ohci = container_of(work, struct ohci_hcd, nec_work);
|
||||
int status;
|
||||
|
||||
status = ohci_init(ohci);
|
||||
if (status != 0) {
|
||||
ohci_err(ohci, "Restarting NEC controller failed in %s, %d\n",
|
||||
"ohci_init", status);
|
||||
return;
|
||||
}
|
||||
|
||||
status = ohci_restart(ohci);
|
||||
if (status != 0)
|
||||
ohci_err(ohci, "Restarting NEC controller failed in %s, %d\n",
|
||||
"ohci_restart", status);
|
||||
}
|
||||
|
||||
static int ohci_quirk_nec(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
|
||||
|
||||
ohci->flags |= OHCI_QUIRK_NEC;
|
||||
INIT_WORK(&ohci->nec_work, ohci_quirk_nec_worker);
|
||||
ohci_dbg (ohci, "enabled NEC chipset lost interrupt quirk\n");
|
||||
|
||||
return 0;
|
||||
|
Reference in New Issue
Block a user