summaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/pci-quirks.c
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2011-11-17 16:41:45 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2011-11-18 11:18:45 -0800
commitc61875977458637226ab093a35d200f2d5789787 (patch)
tree8599601ac6abc0532f11858d572119d260d9925c /drivers/usb/host/pci-quirks.c
parent46b5a277ed90317a4d17e936c16037e76011b219 (diff)
downloadlinux-c61875977458637226ab093a35d200f2d5789787.tar.bz2
OHCI: final fix for NVIDIA problems (I hope)
Problems with NVIDIA's OHCI host controllers persist. After looking carefully through the spec, I finally realized that when a controller is reset it then automatically goes into a SUSPEND state in which it is completely quiescent (no DMA and no IRQs) and from which it will not awaken until the system puts it into the OPERATIONAL state. Therefore there's no need to worry about controllers being in the RESET state for extended periods, or remaining in the OPERATIONAL state during system shutdown. The proper action for device initialization is to put the controller into the RESET state (if it's not there already) and then to issue a software reset. Similarly, the proper action for device shutdown is simply to do a software reset. This patch (as1499) implements such an approach. It simplifies initialization and shutdown, and allows the NVIDIA shutdown-quirk code to be removed. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Tested-by: Andre "Osku" Schmidt <andre.osku.schmidt@googlemail.com> Tested-by: Arno Augustin <Arno.Augustin@web.de> Cc: stable <stable@vger.kernel.org> [after tested in 3.2 for a while] Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/pci-quirks.c')
-rw-r--r--drivers/usb/host/pci-quirks.c50
1 files changed, 22 insertions, 28 deletions
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index c7fd6ce11904..caf87428ca43 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -37,6 +37,7 @@
#define OHCI_INTRENABLE 0x10
#define OHCI_INTRDISABLE 0x14
#define OHCI_FMINTERVAL 0x34
+#define OHCI_HCFS (3 << 6) /* hc functional state */
#define OHCI_HCR (1 << 0) /* host controller reset */
#define OHCI_OCR (1 << 3) /* ownership change request */
#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
@@ -466,6 +467,8 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
{
void __iomem *base;
u32 control;
+ u32 fminterval;
+ int cnt;
if (!mmio_resource_enabled(pdev, 0))
return;
@@ -498,41 +501,32 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
}
#endif
- /* reset controller, preserving RWC (and possibly IR) */
- writel(control & OHCI_CTRL_MASK, base + OHCI_CONTROL);
- readl(base + OHCI_CONTROL);
+ /* disable interrupts */
+ writel((u32) ~0, base + OHCI_INTRDISABLE);
- /* Some NVIDIA controllers stop working if kept in RESET for too long */
- if (pdev->vendor == PCI_VENDOR_ID_NVIDIA) {
- u32 fminterval;
- int cnt;
+ /* Reset the USB bus, if the controller isn't already in RESET */
+ if (control & OHCI_HCFS) {
+ /* Go into RESET, preserving RWC (and possibly IR) */
+ writel(control & OHCI_CTRL_MASK, base + OHCI_CONTROL);
+ readl(base + OHCI_CONTROL);
- /* drive reset for at least 50 ms (7.1.7.5) */
+ /* drive bus reset for at least 50 ms (7.1.7.5) */
msleep(50);
+ }
- /* software reset of the controller, preserving HcFmInterval */
- fminterval = readl(base + OHCI_FMINTERVAL);
- writel(OHCI_HCR, base + OHCI_CMDSTATUS);
+ /* software reset of the controller, preserving HcFmInterval */
+ fminterval = readl(base + OHCI_FMINTERVAL);
+ writel(OHCI_HCR, base + OHCI_CMDSTATUS);
- /* reset requires max 10 us delay */
- for (cnt = 30; cnt > 0; --cnt) { /* ... allow extra time */
- if ((readl(base + OHCI_CMDSTATUS) & OHCI_HCR) == 0)
- break;
- udelay(1);
- }
- writel(fminterval, base + OHCI_FMINTERVAL);
-
- /* Now we're in the SUSPEND state with all devices reset
- * and wakeups and interrupts disabled
- */
+ /* reset requires max 10 us delay */
+ for (cnt = 30; cnt > 0; --cnt) { /* ... allow extra time */
+ if ((readl(base + OHCI_CMDSTATUS) & OHCI_HCR) == 0)
+ break;
+ udelay(1);
}
+ writel(fminterval, base + OHCI_FMINTERVAL);
- /*
- * disable interrupts
- */
- writel(~(u32)0, base + OHCI_INTRDISABLE);
- writel(~(u32)0, base + OHCI_INTRSTATUS);
-
+ /* Now the controller is safely in SUSPEND and nothing can wake it up */
iounmap(base);
}