From: Oliver Neukum Date: Fri, 27 Nov 2009 14:17:59 +0000 (+0100) Subject: USB: work around for EHCI with quirky periodic schedules X-Git-Tag: v2.6.31.7~30 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=77d12b19a0fe01d9e81baae809903ec329f84a15;p=thirdparty%2Fkernel%2Fstable.git USB: work around for EHCI with quirky periodic schedules commit ee4ecb8ac63a5792bec448037d4b82ec4144f94b upstream. a quirky chipset needs periodic schedules to run for a minimum time before they can be disabled again. This enforces the requirement with a time stamp and a calculated delay Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 11c627ce60224..7e4b381716a70 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -655,6 +656,7 @@ static int ehci_run (struct usb_hcd *hcd) ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ msleep(5); up_write(&ehci_cf_port_reset_rwsem); + ehci->last_periodic_enable = ktime_get_real(); temp = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase)); ehci_info (ehci, diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index c2f1b7df918cd..c757a706843c4 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -72,6 +72,12 @@ static int ehci_pci_setup(struct usb_hcd *hcd) int retval; switch (pdev->vendor) { + case PCI_VENDOR_ID_INTEL: + if (pdev->device == 0x27cc) { + ehci->broken_periodic = 1; + ehci_info(ehci, "using broken periodic workaround\n"); + } + break; case PCI_VENDOR_ID_TOSHIBA_2: /* celleb's companion chip */ if (pdev->device == 0x01b5) { diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index edd61ee90323c..c340f1f4b8813 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -456,6 +456,8 @@ static int enable_periodic (struct ehci_hcd *ehci) /* make sure ehci_work scans these */ ehci->next_uframe = ehci_readl(ehci, &ehci->regs->frame_index) % (ehci->periodic_size << 3); + if (unlikely(ehci->broken_periodic)) + ehci->last_periodic_enable = ktime_get_real(); return 0; } @@ -467,6 +469,16 @@ static int disable_periodic (struct ehci_hcd *ehci) if (--ehci->periodic_sched) return 0; + if (unlikely(ehci->broken_periodic)) { + /* delay experimentally determined */ + ktime_t safe = ktime_add_us(ehci->last_periodic_enable, 1000); + ktime_t now = ktime_get_real(); + s64 delay = ktime_us_delta(safe, now); + + if (unlikely(delay > 0)) + udelay(delay); + } + /* did setting PSE not take effect yet? * takes effect only at frame boundaries... */ diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 2bfff30f4704f..aab3cbcc9afb8 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -118,6 +118,7 @@ struct ehci_hcd { /* one per controller */ unsigned stamp; unsigned random_frame; unsigned long next_statechange; + ktime_t last_periodic_enable; u32 command; /* SILICON QUIRKS */ @@ -126,6 +127,7 @@ struct ehci_hcd { /* one per controller */ unsigned big_endian_mmio:1; unsigned big_endian_desc:1; unsigned has_amcc_usb23:1; + unsigned broken_periodic:1; /* required for usb32 quirk */ #define OHCI_CTRL_HCFS (3 << 6)