USB: ehci-dbgp: Execute early BIOS hand off
authorJason Wessel <jason.wessel@windriver.com>
Thu, 20 Aug 2009 20:39:50 +0000 (15:39 -0500)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 23 Sep 2009 13:46:38 +0000 (06:46 -0700)
The PCI quirk code executes a BIOS hand off to obtain full control of
the EHCI host controller, the self contained ehci-dbgp driver must do
the same thing using the early PCI API, else the BIOS can cause a
fatal fault.

Signed-off-by: Jason Wessel <jason.wessel@windriver.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: dbrownell@users.sourceforge.net
Cc: Yinghai Lu <yinghai@kernel.org>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/early/ehci-dbgp.c

index 8de709a..7deae97 100644 (file)
@@ -435,6 +435,53 @@ static void __init detect_set_debug_port(void)
        }
 }
 
+/* The code in early_ehci_bios_handoff() is derived from the usb pci
+ * quirk initialization, but altered so as to use the early PCI
+ * routines. */
+#define EHCI_USBLEGSUP_BIOS    (1 << 16)       /* BIOS semaphore */
+#define EHCI_USBLEGCTLSTS      4               /* legacy control/status */
+static void __init early_ehci_bios_handoff(void)
+{
+       u32 hcc_params = readl(&ehci_caps->hcc_params);
+       int offset = (hcc_params >> 8) & 0xff;
+       u32 cap;
+       int msec;
+
+       if (!offset)
+               return;
+
+       cap = read_pci_config(ehci_dev.bus, ehci_dev.slot,
+                             ehci_dev.func, offset);
+       dbgp_printk("dbgp: ehci BIOS state %08x\n", cap);
+
+       if ((cap & 0xff) == 1 && (cap & EHCI_USBLEGSUP_BIOS)) {
+               dbgp_printk("dbgp: BIOS handoff\n");
+               write_pci_config_byte(ehci_dev.bus, ehci_dev.slot,
+                                     ehci_dev.func, offset + 3, 1);
+       }
+
+       /* if boot firmware now owns EHCI, spin till it hands it over. */
+       msec = 1000;
+       while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) {
+               mdelay(10);
+               msec -= 10;
+               cap = read_pci_config(ehci_dev.bus, ehci_dev.slot,
+                                     ehci_dev.func, offset);
+       }
+
+       if (cap & EHCI_USBLEGSUP_BIOS) {
+               /* well, possibly buggy BIOS... try to shut it down,
+                * and hope nothing goes too wrong */
+               dbgp_printk("dbgp: BIOS handoff failed: %08x\n", cap);
+               write_pci_config_byte(ehci_dev.bus, ehci_dev.slot,
+                                     ehci_dev.func, offset + 2, 0);
+       }
+
+       /* just in case, always disable EHCI SMIs */
+       write_pci_config_byte(ehci_dev.bus, ehci_dev.slot, ehci_dev.func,
+                             offset + EHCI_USBLEGCTLSTS, 0);
+}
+
 static int __init ehci_setup(void)
 {
        struct usb_debug_descriptor dbgp_desc;
@@ -446,6 +493,8 @@ static int __init ehci_setup(void)
        int port_map_tried;
        int playtimes = 3;
 
+       early_ehci_bios_handoff();
+
 try_next_time:
        port_map_tried = 0;