USB: at91-ohci, handle extra at91sam9261 ahb clock
[safe/jmp/linux-2.6] / drivers / usb / host / pci-quirks.c
index e46528c..2086165 100644 (file)
@@ -8,19 +8,13 @@
  *  (and others)
  */
 
-#include <linux/config.h>
-#ifdef CONFIG_USB_DEBUG
-#define DEBUG
-#else
-#undef DEBUG
-#endif
-
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/acpi.h>
+#include "pci-quirks.h"
 
 
 #define UHCI_USBLEGSUP         0xc0            /* legacy support */
@@ -173,8 +167,6 @@ 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;
-       int wait_time;
-       u32 control;
 
        if (!mmio_resource_enabled(pdev, 0))
                return;
@@ -185,9 +177,10 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
 
 /* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
 #ifndef __hppa__
-       control = readl(base + OHCI_CONTROL);
+{
+       u32 control = readl(base + OHCI_CONTROL);
        if (control & OHCI_CTRL_IR) {
-               wait_time = 500; /* arbitrary; 5 seconds */
+               int wait_time = 500; /* arbitrary; 5 seconds */
                writel(OHCI_INTR_OC, base + OHCI_INTRENABLE);
                writel(OHCI_OCR, base + OHCI_CMDSTATUS);
                while (wait_time > 0 &&
@@ -196,13 +189,15 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
                        msleep(10);
                }
                if (wait_time <= 0)
-                       printk(KERN_WARNING "%s %s: early BIOS handoff "
-                                       "failed (BIOS bug ?)\n",
-                                       pdev->dev.bus_id, "OHCI");
+                       printk(KERN_WARNING "%s %s: BIOS handoff "
+                                       "failed (BIOS bug ?) %08x\n",
+                                       pdev->dev.bus_id, "OHCI",
+                                       readl(base + OHCI_CONTROL));
 
                /* reset controller, preserving RWC */
                writel(control & OHCI_CTRL_RWC, base + OHCI_CONTROL);
        }
+}
 #endif
 
        /*
@@ -218,8 +213,9 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev)
 {
        int wait_time, delta;
        void __iomem *base, *op_reg_base;
-       u32 hcc_params, val, temp;
-       u8 cap_length;
+       u32     hcc_params, val;
+       u8      offset, cap_length;
+       int     count = 256/4;
 
        if (!mmio_resource_enabled(pdev, 0))
                return;
@@ -230,51 +226,88 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev)
 
        cap_length = readb(base);
        op_reg_base = base + cap_length;
-       hcc_params = readl(base + EHCI_HCC_PARAMS);
-       hcc_params = (hcc_params >> 8) & 0xff;
-       if (hcc_params) {
-               pci_read_config_dword(pdev,
-                                       hcc_params + EHCI_USBLEGSUP,
-                                       &val);
-               if (((val & 0xff) == 1) && (val & EHCI_USBLEGSUP_BIOS)) {
-                       /*
-                        * Ok, BIOS is in smm mode, try to hand off...
-                        */
-                       pci_read_config_dword(pdev,
-                                               hcc_params + EHCI_USBLEGCTLSTS,
-                                               &temp);
-                       pci_write_config_dword(pdev,
-                                               hcc_params + EHCI_USBLEGCTLSTS,
-                                               temp | EHCI_USBLEGCTLSTS_SOOE);
-                       val |= EHCI_USBLEGSUP_OS;
-                       pci_write_config_dword(pdev,
-                                               hcc_params + EHCI_USBLEGSUP,
-                                               val);
 
-                       wait_time = 500;
-                       do {
-                               msleep(10);
-                               wait_time -= 10;
+       /* EHCI 0.96 and later may have "extended capabilities"
+        * spec section 5.1 explains the bios handoff, e.g. for
+        * booting from USB disk or using a usb keyboard
+        */
+       hcc_params = readl(base + EHCI_HCC_PARAMS);
+       offset = (hcc_params >> 8) & 0xff;
+       while (offset && count--) {
+               u32             cap;
+               int             msec;
+
+               pci_read_config_dword(pdev, offset, &cap);
+               switch (cap & 0xff) {
+               case 1:                 /* BIOS/SMM/... handoff support */
+                       if ((cap & EHCI_USBLEGSUP_BIOS)) {
+                               pr_debug("%s %s: BIOS handoff\n",
+                                               pdev->dev.bus_id, "EHCI");
+
+#if 0
+/* aleksey_gorelov@phoenix.com reports that some systems need SMI forced on,
+ * but that seems dubious in general (the BIOS left it off intentionally)
+ * and is known to prevent some systems from booting.  so we won't do this
+ * unless maybe we can determine when we're on a system that needs SMI forced.
+ */
+                               /* BIOS workaround (?): be sure the
+                                * pre-Linux code receives the SMI
+                                */
                                pci_read_config_dword(pdev,
-                                               hcc_params + EHCI_USBLEGSUP,
+                                               offset + EHCI_USBLEGCTLSTS,
                                                &val);
-                       } while (wait_time && (val & EHCI_USBLEGSUP_BIOS));
-                       if (!wait_time) {
-                               /*
-                                * well, possibly buggy BIOS...
-                                */
-                               printk(KERN_WARNING "%s %s: early BIOS handoff "
-                                               "failed (BIOS bug ?)\n",
-                                       pdev->dev.bus_id, "EHCI");
-                               pci_write_config_dword(pdev,
-                                               hcc_params + EHCI_USBLEGSUP,
-                                               EHCI_USBLEGSUP_OS);
                                pci_write_config_dword(pdev,
-                                               hcc_params + EHCI_USBLEGCTLSTS,
-                                               0);
+                                               offset + EHCI_USBLEGCTLSTS,
+                                               val | EHCI_USBLEGCTLSTS_SOOE);
+#endif
+
+                               /* some systems get upset if this semaphore is
+                                * set for any other reason than forcing a BIOS
+                                * handoff..
+                                */
+                               pci_write_config_byte(pdev, offset + 3, 1);
+                       }
+
+                       /* if boot firmware now owns EHCI, spin till
+                        * it hands it over.
+                        */
+                       msec = 5000;
+                       while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) {
+                               msleep(10);
+                               msec -= 10;
+                               pci_read_config_dword(pdev, offset, &cap);
                        }
+
+                       if (cap & EHCI_USBLEGSUP_BIOS) {
+                               /* well, possibly buggy BIOS... try to shut
+                                * it down, and hope nothing goes too wrong
+                                */
+                               printk(KERN_WARNING "%s %s: BIOS handoff "
+                                               "failed (BIOS bug ?) %08x\n",
+                                       pdev->dev.bus_id, "EHCI", cap);
+                               pci_write_config_byte(pdev, offset + 2, 0);
+                       }
+
+                       /* just in case, always disable EHCI SMIs */
+                       pci_write_config_dword(pdev,
+                                       offset + EHCI_USBLEGCTLSTS,
+                                       0);
+                       break;
+               case 0:                 /* illegal reserved capability */
+                       cap = 0;
+                       /* FALLTHROUGH */
+               default:
+                       printk(KERN_WARNING "%s %s: unrecognized "
+                                       "capability %02x\n",
+                                       pdev->dev.bus_id, "EHCI",
+                                       cap & 0xff);
+                       break;
                }
+               offset = (cap >> 8) & 0xff;
        }
+       if (!count)
+               printk(KERN_DEBUG "%s %s: capability loop?\n",
+                               pdev->dev.bus_id, "EHCI");
 
        /*
         * halt EHCI & disable its interrupts in any case