Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

OHCI: work around for nVidia shutdown problem

This patch (as1417) fixes a problem affecting some (or all) nVidia
chipsets. When the computer is shut down, the OHCI controllers
continue to power the USB buses and evidently they drive a Reset
signal out all their ports. This prevents attached devices from going
to low power. Mouse LEDs stay on, for example, which is disconcerting
for users and a drain on laptop batteries.

The fix involves leaving each OHCI controller in the OPERATIONAL state
during system shutdown rather than putting it in the RESET state.
Although this nominally means the controller is running, in fact it's
not doing very much since all the schedules are all disabled. However
there is ongoing DMA to the Host Controller Communications Area, so
the patch also disables the bus-master capability of all PCI USB
controllers after the shutdown routine runs.

The fix is applied only to nVidia-based PCI OHCI controllers, so it
shouldn't cause problems on systems using other hardware. As an added
safety measure, in case the kernel encounters one of these running
controllers during boot, the patch changes quirk_usb_handoff_ohci()
(which runs early on during PCI discovery) to reset the controller
before anything bad can happen.

Reported-by: Pali Rohár <pali.rohar@gmail.com>
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
CC: David Brownell <david-b@pacbell.net>
Tested-by: Pali Rohár <pali.rohar@gmail.com>
CC: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Alan Stern and committed by
Greg Kroah-Hartman
3df7169e 637ed74f

+41 -9
+3 -1
drivers/usb/core/hcd-pci.c
··· 329 329 return; 330 330 331 331 if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) && 332 - hcd->driver->shutdown) 332 + hcd->driver->shutdown) { 333 333 hcd->driver->shutdown(hcd); 334 + pci_disable_device(dev); 335 + } 334 336 } 335 337 EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown); 336 338
+8 -1
drivers/usb/host/ohci-hcd.c
··· 398 398 399 399 ohci = hcd_to_ohci (hcd); 400 400 ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); 401 - ohci_usb_reset (ohci); 401 + ohci->hc_control = ohci_readl(ohci, &ohci->regs->control); 402 + 403 + /* If the SHUTDOWN quirk is set, don't put the controller in RESET */ 404 + ohci->hc_control &= (ohci->flags & OHCI_QUIRK_SHUTDOWN ? 405 + OHCI_CTRL_RWC | OHCI_CTRL_HCFS : 406 + OHCI_CTRL_RWC); 407 + ohci_writel(ohci, ohci->hc_control, &ohci->regs->control); 408 + 402 409 /* flush the writes */ 403 410 (void) ohci_readl (ohci, &ohci->regs->control); 404 411 }
+18
drivers/usb/host/ohci-pci.c
··· 201 201 return 0; 202 202 } 203 203 204 + /* nVidia controllers continue to drive Reset signalling on the bus 205 + * even after system shutdown, wasting power. This flag tells the 206 + * shutdown routine to leave the controller OPERATIONAL instead of RESET. 207 + */ 208 + static int ohci_quirk_nvidia_shutdown(struct usb_hcd *hcd) 209 + { 210 + struct ohci_hcd *ohci = hcd_to_ohci(hcd); 211 + 212 + ohci->flags |= OHCI_QUIRK_SHUTDOWN; 213 + ohci_dbg(ohci, "enabled nVidia shutdown quirk\n"); 214 + 215 + return 0; 216 + } 217 + 204 218 /* 205 219 * The hardware normally enables the A-link power management feature, which 206 220 * lets the system lower the power consumption in idle states. ··· 345 331 { 346 332 PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4399), 347 333 .driver_data = (unsigned long)ohci_quirk_amd700, 334 + }, 335 + { 336 + PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID), 337 + .driver_data = (unsigned long) ohci_quirk_nvidia_shutdown, 348 338 }, 349 339 350 340 /* FIXME for some of the early AMD 760 southbridges, OHCI
+1
drivers/usb/host/ohci.h
··· 403 403 #define OHCI_QUIRK_HUB_POWER 0x100 /* distrust firmware power/oc setup */ 404 404 #define OHCI_QUIRK_AMD_ISO 0x200 /* ISO transfers*/ 405 405 #define OHCI_QUIRK_AMD_PREFETCH 0x400 /* pre-fetch for ISO transfer */ 406 + #define OHCI_QUIRK_SHUTDOWN 0x800 /* nVidia power bug */ 406 407 // there are also chip quirks/bugs in init logic 407 408 408 409 struct work_struct nec_work; /* Worker for NEC quirk */
+11 -7
drivers/usb/host/pci-quirks.c
··· 169 169 static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) 170 170 { 171 171 void __iomem *base; 172 + u32 control; 172 173 173 174 if (!mmio_resource_enabled(pdev, 0)) 174 175 return; ··· 178 177 if (base == NULL) 179 178 return; 180 179 180 + control = readl(base + OHCI_CONTROL); 181 + 181 182 /* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */ 182 - #ifndef __hppa__ 183 - { 184 - u32 control = readl(base + OHCI_CONTROL); 183 + #ifdef __hppa__ 184 + #define OHCI_CTRL_MASK (OHCI_CTRL_RWC | OHCI_CTRL_IR) 185 + #else 186 + #define OHCI_CTRL_MASK OHCI_CTRL_RWC 187 + 185 188 if (control & OHCI_CTRL_IR) { 186 189 int wait_time = 500; /* arbitrary; 5 seconds */ 187 190 writel(OHCI_INTR_OC, base + OHCI_INTRENABLE); ··· 199 194 dev_warn(&pdev->dev, "OHCI: BIOS handoff failed" 200 195 " (BIOS bug?) %08x\n", 201 196 readl(base + OHCI_CONTROL)); 202 - 203 - /* reset controller, preserving RWC */ 204 - writel(control & OHCI_CTRL_RWC, base + OHCI_CONTROL); 205 197 } 206 - } 207 198 #endif 199 + 200 + /* reset controller, preserving RWC (and possibly IR) */ 201 + writel(control & OHCI_CTRL_MASK, base + OHCI_CONTROL); 208 202 209 203 /* 210 204 * disable interrupts