USB: dbgp: EHCI debug controller initialization delays
[linux-2.6.git] / drivers / usb / early / ehci-dbgp.c
index 821b7b21c29c2e366b4720be02a118104b96912d..6198ebded3a4c70e3f95998ceedcfee77e5ad12a 100644 (file)
@@ -9,6 +9,12 @@
 #include <asm/pci-direct.h>
 #include <asm/fixmap.h>
 
+#ifdef DBGP_DEBUG
+# define dbgp_printk printk
+#else
+static inline void dbgp_printk(const char *fmt, ...) { }
+#endif
+
 static struct ehci_caps __iomem *ehci_caps;
 static struct ehci_regs __iomem *ehci_regs;
 static struct ehci_dbg_port __iomem *ehci_debug;
@@ -342,6 +348,7 @@ static int __init ehci_reset_port(int port)
        u32 delay_time, delay;
        int loop;
 
+       dbgp_printk("ehci_reset_port %i\n", port);
        /* Reset the usb debug port */
        portsc = readl(&ehci_regs->port_status[port - 1]);
        portsc &= ~PORT_PE;
@@ -352,14 +359,17 @@ static int __init ehci_reset_port(int port)
        for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT;
             delay_time += delay) {
                dbgp_mdelay(delay);
-
                portsc = readl(&ehci_regs->port_status[port - 1]);
+               if (!(portsc & PORT_RESET))
+                       break;
+       }
                if (portsc & PORT_RESET) {
                        /* force reset to complete */
-                       loop = 2;
+                       loop = 100 * 1000;
                        writel(portsc & ~(PORT_RWC_BITS | PORT_RESET),
                                &ehci_regs->port_status[port - 1]);
                        do {
+                               udelay(1);
                                portsc = readl(&ehci_regs->port_status[port-1]);
                        } while ((portsc & PORT_RESET) && (--loop > 0));
                }
@@ -375,7 +385,6 @@ static int __init ehci_reset_port(int port)
                /* If we've finished resetting, then break out of the loop */
                if (!(portsc & PORT_RESET) && (portsc & PORT_PE))
                        return 0;
-       }
        return -EBUSY;
 }
 
@@ -384,24 +393,18 @@ static int __init ehci_wait_for_port(int port)
        u32 status;
        int ret, reps;
 
-       for (reps = 0; reps < 3; reps++) {
-               dbgp_mdelay(100);
+       for (reps = 0; reps < 300; reps++) {
                status = readl(&ehci_regs->status);
-               if (status & STS_PCD) {
-                       ret = ehci_reset_port(port);
-                       if (ret == 0)
-                               return 0;
-               }
+               if (status & STS_PCD)
+                       break;
+               dbgp_mdelay(1);
        }
+       ret = ehci_reset_port(port);
+       if (ret == 0)
+               return 0;
        return -ENOTCONN;
 }
 
-#ifdef DBGP_DEBUG
-# define dbgp_printk early_printk
-#else
-static inline void dbgp_printk(const char *fmt, ...) { }
-#endif
-
 typedef void (*set_debug_port_t)(int port);
 
 static void __init default_set_debug_port(int port)
@@ -435,6 +438,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 +496,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;
 
@@ -471,7 +523,7 @@ try_next_port:
                return -1;
        }
 
-       loop = 100000;
+       loop = 250 * 1000;
        /* Reset the EHCI controller */
        cmd = readl(&ehci_regs->command);
        cmd |= CMD_RESET;
@@ -491,6 +543,7 @@ try_next_port:
        ctrl |= DBGP_OWNER;
        ctrl &= ~(DBGP_ENABLED | DBGP_INUSE);
        writel(ctrl, &ehci_debug->control);
+       udelay(1);
 
        /* Start the ehci running */
        cmd = readl(&ehci_regs->command);
@@ -505,10 +558,13 @@ try_next_port:
        loop = 10;
        do {
                status = readl(&ehci_regs->status);
-       } while ((status & STS_HALT) && (--loop > 0));
+               if (!(status & STS_HALT))
+                       break;
+               udelay(1);
+       } while (--loop > 0);
 
        if (!loop) {
-               dbgp_printk("ehci can be started\n");
+               dbgp_printk("ehci can not be started\n");
                return -1;
        }
        dbgp_printk("ehci started\n");
@@ -700,17 +756,27 @@ int __init early_dbgp_init(char *s)
 static void early_dbgp_write(struct console *con, const char *str, u32 n)
 {
        int chunk, ret;
+       char buf[DBGP_MAX_PACKET];
+       int use_cr = 0;
 
        if (!ehci_debug)
                return;
        while (n > 0) {
-               chunk = n;
-               if (chunk > DBGP_MAX_PACKET)
-                       chunk = DBGP_MAX_PACKET;
+               for (chunk = 0; chunk < DBGP_MAX_PACKET && n > 0;
+                    str++, chunk++, n--) {
+                       if (!use_cr && *str == '\n') {
+                               use_cr = 1;
+                               buf[chunk] = '\r';
+                               str--;
+                               n++;
+                               continue;
+                       }
+                       if (use_cr)
+                               use_cr = 0;
+                       buf[chunk] = *str;
+               }
                ret = dbgp_bulk_write(USB_DEBUG_DEVNUM,
-                       dbgp_endpoint_out, str, chunk);
-               str += chunk;
-               n -= chunk;
+                       dbgp_endpoint_out, buf, chunk);
        }
 }