summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/core/hcd-pci.c4
-rw-r--r--drivers/usb/host/ohci-hcd.c9
-rw-r--r--drivers/usb/host/ohci-pci.c18
-rw-r--r--drivers/usb/host/ohci.h1
-rw-r--r--drivers/usb/host/pci-quirks.c18
5 files changed, 41 insertions, 9 deletions
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index c3f98543caaf..3799573bd385 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -329,8 +329,10 @@ void usb_hcd_pci_shutdown(struct pci_dev *dev)
return;
if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) &&
- hcd->driver->shutdown)
+ hcd->driver->shutdown) {
hcd->driver->shutdown(hcd);
+ pci_disable_device(dev);
+ }
}
EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown);
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index c3b4ccc7337b..8ef3c1944364 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -398,7 +398,14 @@ ohci_shutdown (struct usb_hcd *hcd)
ohci = hcd_to_ohci (hcd);
ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
- ohci_usb_reset (ohci);
+ ohci->hc_control = ohci_readl(ohci, &ohci->regs->control);
+
+ /* If the SHUTDOWN quirk is set, don't put the controller in RESET */
+ ohci->hc_control &= (ohci->flags & OHCI_QUIRK_SHUTDOWN ?
+ OHCI_CTRL_RWC | OHCI_CTRL_HCFS :
+ OHCI_CTRL_RWC);
+ ohci_writel(ohci, ohci->hc_control, &ohci->regs->control);
+
/* flush the writes */
(void) ohci_readl (ohci, &ohci->regs->control);
}
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index 6bdc8b25a6a1..36ee9a666e93 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -201,6 +201,20 @@ static int ohci_quirk_amd700(struct usb_hcd *hcd)
return 0;
}
+/* nVidia controllers continue to drive Reset signalling on the bus
+ * even after system shutdown, wasting power. This flag tells the
+ * shutdown routine to leave the controller OPERATIONAL instead of RESET.
+ */
+static int ohci_quirk_nvidia_shutdown(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+
+ ohci->flags |= OHCI_QUIRK_SHUTDOWN;
+ ohci_dbg(ohci, "enabled nVidia shutdown quirk\n");
+
+ return 0;
+}
+
/*
* The hardware normally enables the A-link power management feature, which
* lets the system lower the power consumption in idle states.
@@ -332,6 +346,10 @@ static const struct pci_device_id ohci_pci_quirks[] = {
PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4399),
.driver_data = (unsigned long)ohci_quirk_amd700,
},
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID),
+ .driver_data = (unsigned long) ohci_quirk_nvidia_shutdown,
+ },
/* FIXME for some of the early AMD 760 southbridges, OHCI
* won't work at all. blacklist them.
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index 5bf15fed0d9f..51facb985c84 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -403,6 +403,7 @@ struct ohci_hcd {
#define OHCI_QUIRK_HUB_POWER 0x100 /* distrust firmware power/oc setup */
#define OHCI_QUIRK_AMD_ISO 0x200 /* ISO transfers*/
#define OHCI_QUIRK_AMD_PREFETCH 0x400 /* pre-fetch for ISO transfer */
+#define OHCI_QUIRK_SHUTDOWN 0x800 /* nVidia power bug */
// there are also chip quirks/bugs in init logic
struct work_struct nec_work; /* Worker for NEC quirk */
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index 83b5f9cea85a..464ed977b45d 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -169,6 +169,7 @@ static int __devinit mmio_resource_enabled(struct pci_dev *pdev, int idx)
static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
{
void __iomem *base;
+ u32 control;
if (!mmio_resource_enabled(pdev, 0))
return;
@@ -177,10 +178,14 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
if (base == NULL)
return;
+ control = readl(base + OHCI_CONTROL);
+
/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
-#ifndef __hppa__
-{
- u32 control = readl(base + OHCI_CONTROL);
+#ifdef __hppa__
+#define OHCI_CTRL_MASK (OHCI_CTRL_RWC | OHCI_CTRL_IR)
+#else
+#define OHCI_CTRL_MASK OHCI_CTRL_RWC
+
if (control & OHCI_CTRL_IR) {
int wait_time = 500; /* arbitrary; 5 seconds */
writel(OHCI_INTR_OC, base + OHCI_INTRENABLE);
@@ -194,13 +199,12 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
dev_warn(&pdev->dev, "OHCI: BIOS handoff failed"
" (BIOS bug?) %08x\n",
readl(base + OHCI_CONTROL));
-
- /* reset controller, preserving RWC */
- writel(control & OHCI_CTRL_RWC, base + OHCI_CONTROL);
}
-}
#endif
+ /* reset controller, preserving RWC (and possibly IR) */
+ writel(control & OHCI_CTRL_MASK, base + OHCI_CONTROL);
+
/*
* disable interrupts
*/