USB: Fix s3c2410_udc usb speed handling
[safe/jmp/linux-2.6] / drivers / usb / host / pci-quirks.c
index b7fd3f6..ae6e70e 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 */
@@ -50,6 +44,7 @@
 #define EHCI_USBSTS            4               /* status register */
 #define EHCI_USBSTS_HALTED     (1 << 12)       /* HCHalted bit */
 #define EHCI_USBINTR           8               /* interrupt register */
+#define EHCI_CONFIGFLAG                0x40            /* configured flag register */
 #define EHCI_USBLEGSUP         0               /* legacy support register */
 #define EHCI_USBLEGSUP_BIOS    (1 << 16)       /* BIOS semaphore */
 #define EHCI_USBLEGSUP_OS      (1 << 24)       /* OS semaphore */
@@ -111,7 +106,7 @@ int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base)
        pci_read_config_word(pdev, UHCI_USBLEGSUP, &legsup);
        if (legsup & ~(UHCI_USBLEGSUP_RO | UHCI_USBLEGSUP_RWC)) {
                dev_dbg(&pdev->dev, "%s: legsup = 0x%04x\n",
-                               __FUNCTION__, legsup);
+                               __func__, legsup);
                goto reset_needed;
        }
 
@@ -119,14 +114,14 @@ int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base)
        if ((cmd & UHCI_USBCMD_RUN) || !(cmd & UHCI_USBCMD_CONFIGURE) ||
                        !(cmd & UHCI_USBCMD_EGSM)) {
                dev_dbg(&pdev->dev, "%s: cmd = 0x%04x\n",
-                               __FUNCTION__, cmd);
+                               __func__, cmd);
                goto reset_needed;
        }
 
        intr = inw(base + UHCI_USBINTR);
        if (intr & (~UHCI_USBINTR_RESUME)) {
                dev_dbg(&pdev->dev, "%s: intr = 0x%04x\n",
-                               __FUNCTION__, intr);
+                               __func__, intr);
                goto reset_needed;
        }
        return 0;
@@ -138,11 +133,23 @@ reset_needed:
 }
 EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc);
 
+static inline int io_type_enabled(struct pci_dev *pdev, unsigned int mask)
+{
+       u16 cmd;
+       return !pci_read_config_word(pdev, PCI_COMMAND, &cmd) && (cmd & mask);
+}
+
+#define pio_enabled(dev) io_type_enabled(dev, PCI_COMMAND_IO)
+#define mmio_enabled(dev) io_type_enabled(dev, PCI_COMMAND_MEMORY)
+
 static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev)
 {
        unsigned long base = 0;
        int i;
 
+       if (!pio_enabled(pdev))
+               return;
+
        for (i = 0; i < PCI_ROM_RESOURCE; i++)
                if ((pci_resource_flags(pdev, i) & IORESOURCE_IO)) {
                        base = pci_resource_start(pdev, i);
@@ -153,11 +160,17 @@ static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev)
                uhci_check_and_reset_hc(pdev, base);
 }
 
+static int __devinit mmio_resource_enabled(struct pci_dev *pdev, int idx)
+{
+       return pci_resource_start(pdev, idx) && mmio_enabled(pdev);
+}
+
 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;
 
        base = ioremap_nocache(pci_resource_start(pdev, 0),
                                     pci_resource_len(pdev, 0));
@@ -165,9 +178,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 &&
@@ -176,13 +190,14 @@ 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");
+                       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
 
        /*
@@ -198,8 +213,13 @@ 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;
+       int     tried_handoff = 0;
+
+       if (!mmio_resource_enabled(pdev, 0))
+               return;
 
        base = ioremap_nocache(pci_resource_start(pdev, 0),
                                pci_resource_len(pdev, 0));
@@ -207,51 +227,90 @@ 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)) {
+                               dev_dbg(&pdev->dev, "EHCI: BIOS handoff\n");
+
+#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)) {
+                               tried_handoff = 1;
+                               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
+                                */
+                               dev_warn(&pdev->dev, "EHCI: BIOS handoff failed"
+                                               " (BIOS bug?) %08x\n", 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);
+
+                       /* If the BIOS ever owned the controller then we
+                        * can't expect any power sessions to remain intact.
+                        */
+                       if (tried_handoff)
+                               writel(0, op_reg_base + EHCI_CONFIGFLAG);
+                       break;
+               case 0:                 /* illegal reserved capability */
+                       cap = 0;
+                       /* FALLTHROUGH */
+               default:
+                       dev_warn(&pdev->dev, "EHCI: unrecognized capability "
+                                       "%02x\n", cap & 0xff);
+                       break;
                }
+               offset = (cap >> 8) & 0xff;
        }
+       if (!count)
+               dev_printk(KERN_DEBUG, &pdev->dev, "EHCI: capability loop?\n");
 
        /*
         * halt EHCI & disable its interrupts in any case
@@ -293,4 +352,4 @@ static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev)
        else if (pdev->class == PCI_CLASS_SERIAL_USB_EHCI)
                quirk_usb_disable_ehci(pdev);
 }
-DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff);
+DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff);