fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / usb / host / ohci-hcd.c
index 0609e12..c1c1d87 100644 (file)
@@ -2,85 +2,21 @@
  * OHCI HCD (Host Controller Driver) for USB.
  *
  * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
- * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
- * 
+ * (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net>
+ *
  * [ Initialisation is based on Linus'  ]
  * [ uhci code and gregs ohci fragments ]
  * [ (C) Copyright 1999 Linus Torvalds  ]
  * [ (C) Copyright 1999 Gregory P. Smith]
- * 
- * 
+ *
+ *
  * OHCI is the main "non-Intel/VIA" standard for USB 1.1 host controller
  * interfaces (though some non-x86 Intel chips use it).  It supports
  * smarter hardware than UHCI.  A download link for the spec available
  * through the http://www.usb.org website.
  *
- * History:
- * 
- * 2004/03/24 LH7A404 support (Durgesh Pattamatta & Marc Singer)
- * 2004/02/04 use generic dma_* functions instead of pci_* (dsaxena@plexity.net)
- * 2003/02/24 show registers in sysfs (Kevin Brosius)
- *
- * 2002/09/03 get rid of ed hashtables, rework periodic scheduling and
- *     bandwidth accounting; if debugging, show schedules in driverfs
- * 2002/07/19 fixes to management of ED and schedule state.
- * 2002/06/09 SA-1111 support (Christopher Hoover)
- * 2002/06/01 remember frame when HC won't see EDs any more; use that info
- *     to fix urb unlink races caused by interrupt latency assumptions;
- *     minor ED field and function naming updates
- * 2002/01/18 package as a patch for 2.5.3; this should match the
- *     2.4.17 kernel modulo some bugs being fixed.
- *
- * 2001/10/18 merge pmac cleanup (Benjamin Herrenschmidt) and bugfixes
- *     from post-2.4.5 patches.
- * 2001/09/20 URB_ZERO_PACKET support; hcca_dma portability, OPTi warning
- * 2001/09/07 match PCI PM changes, errnos from Linus' tree
- * 2001/05/05 fork 2.4.5 version into "hcd" framework, cleanup, simplify;
- *     pbook pci quirks gone (please fix pbook pci sw!) (db)
- *
- * 2001/04/08 Identify version on module load (gb)
- * 2001/03/24 td/ed hashing to remove bus_to_virt (Steve Longerbeam);
-       pci_map_single (db)
- * 2001/03/21 td and dev/ed allocation uses new pci_pool API (db)
- * 2001/03/07 hcca allocation uses pci_alloc_consistent (Steve Longerbeam)
- *
- * 2000/09/26 fixed races in removing the private portion of the urb
- * 2000/09/07 disable bulk and control lists when unlinking the last
- *     endpoint descriptor in order to avoid unrecoverable errors on
- *     the Lucent chips. (rwc@sgi)
- * 2000/08/29 use bandwidth claiming hooks (thanks Randy!), fix some
- *     urb unlink probs, indentation fixes
- * 2000/08/11 various oops fixes mostly affecting iso and cleanup from
- *     device unplugs.
- * 2000/06/28 use PCI hotplug framework, for better power management
- *     and for Cardbus support (David Brownell)
- * 2000/earlier:  fixes for NEC/Lucent chips; suspend/resume handling
- *     when the controller loses power; handle UE; cleanup; ...
- *
- * v5.2 1999/12/07 URB 3rd preview, 
- * v5.1 1999/11/30 URB 2nd preview, cpia, (usb-scsi)
- * v5.0 1999/11/22 URB Technical preview, Paul Mackerras powerbook susp/resume 
- *     i386: HUB, Keyboard, Mouse, Printer 
- *
- * v4.3 1999/10/27 multiple HCs, bulk_request
- * v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes
- * v4.1 1999/08/27 Randy Dunlap's - ISO API first impl.
- * v4.0 1999/08/18 
- * v3.0 1999/06/25 
- * v2.1 1999/05/09  code clean up
- * v2.0 1999/05/04 
- * v1.0 1999/04/27 initial release
- *
  * This file is licenced under the GPL.
  */
-#include <linux/config.h>
-
-#ifdef CONFIG_USB_DEBUG
-#      define DEBUG
-#else
-#      undef DEBUG
-#endif
 
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/timer.h>
 #include <linux/list.h>
-#include <linux/interrupt.h>  /* for in_interrupt () */
 #include <linux/usb.h>
-#include <linux/usb_otg.h>
-#include "../core/hcd.h"
-#include <linux/dma-mapping.h> 
-#include <linux/dmapool.h>    /* needed by ohci-mem.c when no PCI */
+#include <linux/usb/otg.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/reboot.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/unaligned.h>
 #include <asm/byteorder.h>
 
+#include "../core/hcd.h"
 
-#define DRIVER_VERSION "2004 Feb 02"
+#define DRIVER_VERSION "2006 August 04"
 #define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
 #define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
 
 /*-------------------------------------------------------------------------*/
 
-// #define OHCI_VERBOSE_DEBUG  /* not always helpful */
+#undef OHCI_VERBOSE_DEBUG      /* not always helpful */
 
 /* For initializing controller (mask in an HCFS mode too) */
-#define        OHCI_CONTROL_INIT       OHCI_CTRL_CBSR
+#define        OHCI_CONTROL_INIT       OHCI_CTRL_CBSR
 #define        OHCI_INTR_INIT \
-       (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_WDH)
+               (OHCI_INTR_MIE | OHCI_INTR_RHSC | OHCI_INTR_UE \
+               | OHCI_INTR_RD | OHCI_INTR_WDH)
+
+#ifdef __hppa__
+/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
+#define        IR_DISABLE
+#endif
+
+#ifdef CONFIG_ARCH_OMAP
+/* OMAP doesn't support IR (no SMM; not needed) */
+#define        IR_DISABLE
+#endif
 
 /*-------------------------------------------------------------------------*/
 
 static const char      hcd_name [] = "ohci_hcd";
 
+#define        STATECHANGE_DELAY       msecs_to_jiffies(300)
+
 #include "ohci.h"
 
+static void ohci_dump (struct ohci_hcd *ohci, int verbose);
+static int ohci_init (struct ohci_hcd *ohci);
+static void ohci_stop (struct usb_hcd *hcd);
+
 #include "ohci-hub.c"
 #include "ohci-dbg.c"
 #include "ohci-mem.c"
 #include "ohci-q.c"
 
 
-/* Some boards don't support per-port power switching */
-static int power_switching = 0;
-module_param (power_switching, bool, 0);
-MODULE_PARM_DESC (power_switching, "true (not default) to switch port power");
+/*
+ * On architectures with edge-triggered interrupts we must never return
+ * IRQ_NONE.
+ */
+#if defined(CONFIG_SA1111)  /* ... or other edge-triggered systems */
+#define IRQ_NOTMINE    IRQ_HANDLED
+#else
+#define IRQ_NOTMINE    IRQ_NONE
+#endif
+
+
+/* Some boards misreport power switching/overcurrent */
+static int distrust_firmware = 1;
+module_param (distrust_firmware, bool, 0);
+MODULE_PARM_DESC (distrust_firmware,
+       "true to distrust firmware power/overcurrent setup");
+
+/* Some boards leave IR set wrongly, since they fail BIOS/SMM handshakes */
+static int no_handshake = 0;
+module_param (no_handshake, bool, 0);
+MODULE_PARM_DESC (no_handshake, "true (not default) disables BIOS handshake");
 
 /*-------------------------------------------------------------------------*/
 
@@ -146,8 +116,9 @@ MODULE_PARM_DESC (power_switching, "true (not default) to switch port power");
  */
 static int ohci_urb_enqueue (
        struct usb_hcd  *hcd,
+       struct usb_host_endpoint *ep,
        struct urb      *urb,
-       int             mem_flags
+       gfp_t           mem_flags
 ) {
        struct ohci_hcd *ohci = hcd_to_ohci (hcd);
        struct ed       *ed;
@@ -156,13 +127,13 @@ static int ohci_urb_enqueue (
        int             i, size = 0;
        unsigned long   flags;
        int             retval = 0;
-       
+
 #ifdef OHCI_VERBOSE_DEBUG
        urb_print (urb, "SUB", usb_pipein (pipe));
 #endif
-       
+
        /* every endpoint has a ed, locate and maybe (re)initialize it */
-       if (! (ed = ed_get (ohci, urb->dev, pipe, urb->interval)))
+       if (! (ed = ed_get (ohci, ep, urb->dev, pipe, urb->interval)))
                return -ENOMEM;
 
        /* for the private part of the URB we need the number of TDs (size) */
@@ -205,7 +176,7 @@ static int ohci_urb_enqueue (
        memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (struct td *));
        INIT_LIST_HEAD (&urb_priv->pending);
        urb_priv->length = size;
-       urb_priv->ed = ed;      
+       urb_priv->ed = ed;
 
        /* allocate the TDs (deferring hash chain updates) */
        for (i = 0; i < size; i++) {
@@ -215,12 +186,16 @@ static int ohci_urb_enqueue (
                        urb_free_priv (ohci, urb_priv);
                        return -ENOMEM;
                }
-       }       
+       }
 
        spin_lock_irqsave (&ohci->lock, flags);
 
        /* don't submit to a dead HC */
-       if (!HCD_IS_RUNNING(ohci->hcd.state)) {
+       if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+               retval = -ENODEV;
+               goto fail;
+       }
+       if (!HC_IS_RUNNING(hcd->state)) {
                retval = -ENODEV;
                goto fail;
        }
@@ -230,7 +205,7 @@ static int ohci_urb_enqueue (
        if (urb->status != -EINPROGRESS) {
                spin_unlock (&urb->lock);
                urb->hcpriv = urb_priv;
-               finish_urb (ohci, urb, NULL);
+               finish_urb (ohci, urb);
                retval = 0;
                goto fail;
        }
@@ -241,7 +216,7 @@ static int ohci_urb_enqueue (
                if (retval < 0)
                        goto fail0;
                if (ed->type == PIPE_ISOCHRONOUS) {
-                       u16     frame = OHCI_FRAME_NO(ohci->hcca);
+                       u16     frame = ohci_frame_no(ohci);
 
                        /* delay a few frames before the first TD */
                        frame += max_t (u16, 8, ed->interval);
@@ -282,13 +257,13 @@ static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
 {
        struct ohci_hcd         *ohci = hcd_to_ohci (hcd);
        unsigned long           flags;
-       
+
 #ifdef OHCI_VERBOSE_DEBUG
        urb_print (urb, "UNLINK", 1);
-#endif           
+#endif
 
        spin_lock_irqsave (&ohci->lock, flags);
-       if (HCD_IS_RUNNING(ohci->hcd.state)) {
+       if (HC_IS_RUNNING(hcd->state)) {
                urb_priv_t  *urb_priv;
 
                /* Unless an IRQ completed the unlink while it was being
@@ -306,7 +281,7 @@ static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
                 * any more ... just clean up every urb's memory.
                 */
                if (urb->hcpriv)
-                       finish_urb (ohci, urb, NULL);
+                       finish_urb (ohci, urb);
        }
        spin_unlock_irqrestore (&ohci->lock, flags);
        return 0;
@@ -319,31 +294,26 @@ static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
  */
 
 static void
-ohci_endpoint_disable (struct usb_hcd *hcd, struct hcd_dev *dev, int ep)
+ohci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
 {
        struct ohci_hcd         *ohci = hcd_to_ohci (hcd);
-       int                     epnum = ep & USB_ENDPOINT_NUMBER_MASK;
        unsigned long           flags;
-       struct ed               *ed;
+       struct ed               *ed = ep->hcpriv;
        unsigned                limit = 1000;
 
        /* ASSERT:  any requests/urbs are being unlinked */
        /* ASSERT:  nobody can be submitting urbs for this any more */
 
-       epnum <<= 1;
-       if (epnum != 0 && !(ep & USB_DIR_IN))
-               epnum |= 1;
+       if (!ed)
+               return;
 
 rescan:
        spin_lock_irqsave (&ohci->lock, flags);
-       ed = dev->ep [epnum];
-       if (!ed)
-               goto done;
 
-       if (!HCD_IS_RUNNING (ohci->hcd.state)) {
+       if (!HC_IS_RUNNING (hcd->state)) {
 sanitize:
                ed->state = ED_IDLE;
-               finish_unlinks (ohci, 0, NULL);
+               finish_unlinks (ohci, 0);
        }
 
        switch (ed->state) {
@@ -354,8 +324,7 @@ sanitize:
                        goto sanitize;
                }
                spin_unlock_irqrestore (&ohci->lock, flags);
-               set_current_state (TASK_UNINTERRUPTIBLE);
-               schedule_timeout (1);
+               schedule_timeout_uninterruptible(1);
                goto rescan;
        case ED_IDLE:           /* fully unlinked */
                if (list_empty (&ed->td_list)) {
@@ -368,14 +337,13 @@ sanitize:
                /* caller was supposed to have unlinked any requests;
                 * that's not our job.  can't recover; must leak ed.
                 */
-               ohci_err (ohci, "leak ed %p (#%d) state %d%s\n",
-                       ed, epnum, ed->state,
+               ohci_err (ohci, "leak ed %p (#%02x) state %d%s\n",
+                       ed, ep->desc.bEndpointAddress, ed->state,
                        list_empty (&ed->td_list) ? "" : " (has tds)");
                td_free (ohci, ed->dummy);
                break;
        }
-       dev->ep [epnum] = NULL;
-done:
+       ep->hcpriv = NULL;
        spin_unlock_irqrestore (&ohci->lock, flags);
        return;
 }
@@ -384,34 +352,56 @@ static int ohci_get_frame (struct usb_hcd *hcd)
 {
        struct ohci_hcd         *ohci = hcd_to_ohci (hcd);
 
-       return OHCI_FRAME_NO(ohci->hcca);
+       return ohci_frame_no(ohci);
+}
+
+static void ohci_usb_reset (struct ohci_hcd *ohci)
+{
+       ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
+       ohci->hc_control &= OHCI_CTRL_RWC;
+       ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+}
+
+/* ohci_shutdown forcibly disables IRQs and DMA, helping kexec and
+ * other cases where the next software may expect clean state from the
+ * "firmware".  this is bus-neutral, unlike shutdown() methods.
+ */
+static void
+ohci_shutdown (struct usb_hcd *hcd)
+{
+       struct ohci_hcd *ohci;
+
+       ohci = hcd_to_ohci (hcd);
+       ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
+       ohci_usb_reset (ohci);
+       /* flush the writes */
+       (void) ohci_readl (ohci, &ohci->regs->control);
 }
 
 /*-------------------------------------------------------------------------*
  * HC functions
  *-------------------------------------------------------------------------*/
 
-/* reset the HC and BUS */
+/* init memory, and kick BIOS/SMM off */
 
-static int hc_reset (struct ohci_hcd *ohci)
+static int ohci_init (struct ohci_hcd *ohci)
 {
-       u32 temp;
+       int ret;
+       struct usb_hcd *hcd = ohci_to_hcd(ohci);
 
-       /* boot firmware should have set this up (5.1.1.3.1) */
-       if (!ohci->fminterval) {
-               temp = ohci_readl (&ohci->regs->fminterval);
-               if (temp & 0x3fff0000)
-                       ohci->fminterval = temp;
-               else
-                       ohci->fminterval = DEFAULT_FMINTERVAL;
-               /* also: power/overcurrent flags in roothub.a */
-       }
+       disable (ohci);
+       ohci->regs = hcd->regs;
 
-       /* SMM owns the HC?  not for long!
-        * On PA-RISC, PDC can leave IR set incorrectly; ignore it there.
+       /* REVISIT this BIOS handshake is now moved into PCI "quirks", and
+        * was never needed for most non-PCI systems ... remove the code?
         */
-#ifndef __hppa__
-       if (ohci_readl (&ohci->regs->control) & OHCI_CTRL_IR) {
+
+#ifndef IR_DISABLE
+       /* SMM owns the HC?  not for long! */
+       if (!no_handshake && ohci_readl (ohci,
+                                       &ohci->regs->control) & OHCI_CTRL_IR) {
+               u32 temp;
+
                ohci_dbg (ohci, "USB HC TakeOver from BIOS/SMM\n");
 
                /* this timeout is arbitrary.  we make it long, so systems
@@ -420,50 +410,128 @@ static int hc_reset (struct ohci_hcd *ohci)
                 */
                temp = 500;     /* arbitrary: five seconds */
 
-               writel (OHCI_INTR_OC, &ohci->regs->intrenable);
-               writel (OHCI_OCR, &ohci->regs->cmdstatus);
-               while (ohci_readl (&ohci->regs->control) & OHCI_CTRL_IR) {
+               ohci_writel (ohci, OHCI_INTR_OC, &ohci->regs->intrenable);
+               ohci_writel (ohci, OHCI_OCR, &ohci->regs->cmdstatus);
+               while (ohci_readl (ohci, &ohci->regs->control) & OHCI_CTRL_IR) {
                        msleep (10);
                        if (--temp == 0) {
-                               ohci_err (ohci, "USB HC TakeOver failed!\n");
-                               return -1;
+                               ohci_err (ohci, "USB HC takeover failed!"
+                                       "  (BIOS/SMM bug)\n");
+                               return -EBUSY;
                        }
                }
+               ohci_usb_reset (ohci);
        }
 #endif
 
        /* Disable HC interrupts */
-       writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
+       ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
 
-       ohci_dbg (ohci, "reset, control = 0x%x\n",
-                 ohci_readl (&ohci->regs->control));
+       /* flush the writes, and save key bits like RWC */
+       if (ohci_readl (ohci, &ohci->regs->control) & OHCI_CTRL_RWC)
+               ohci->hc_control |= OHCI_CTRL_RWC;
 
-       /* Reset USB (needed by some controllers); RemoteWakeupConnected
-        * saved if boot firmware (BIOS/SMM/...) told us it's connected
-        * (for OHCI integrated on mainboard, it normally is)
-        */
-       ohci->hc_control = ohci_readl (&ohci->regs->control);
-       ohci->hc_control &= OHCI_CTRL_RWC;      /* hcfs 0 = RESET */
-       if (ohci->hc_control)
-               ohci->hcd.can_wakeup = 1;
-       writel (ohci->hc_control, &ohci->regs->control);
-       if (power_switching) {
-               unsigned ports = roothub_a (ohci) & RH_A_NDP; 
+       /* Read the number of ports unless overridden */
+       if (ohci->num_ports == 0)
+               ohci->num_ports = roothub_a(ohci) & RH_A_NDP;
+
+       if (ohci->hcca)
+               return 0;
+
+       ohci->hcca = dma_alloc_coherent (hcd->self.controller,
+                       sizeof *ohci->hcca, &ohci->hcca_dma, 0);
+       if (!ohci->hcca)
+               return -ENOMEM;
+
+       if ((ret = ohci_mem_init (ohci)) < 0)
+               ohci_stop (hcd);
+       else {
+               create_debug_files (ohci);
+       }
+
+       return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Start an OHCI controller, set the BUS operational
+ * resets USB and controller
+ * enable interrupts
+ */
+static int ohci_run (struct ohci_hcd *ohci)
+{
+       u32                     mask, temp;
+       int                     first = ohci->fminterval == 0;
+       struct usb_hcd          *hcd = ohci_to_hcd(ohci);
+
+       disable (ohci);
 
+       /* boot firmware should have set this up (5.1.1.3.1) */
+       if (first) {
+
+               temp = ohci_readl (ohci, &ohci->regs->fminterval);
+               ohci->fminterval = temp & 0x3fff;
+               if (ohci->fminterval != FI)
+                       ohci_dbg (ohci, "fminterval delta %d\n",
+                               ohci->fminterval - FI);
+               ohci->fminterval |= FSMP (ohci->fminterval) << 16;
+               /* also: power/overcurrent flags in roothub.a */
+       }
+
+       /* Reset USB nearly "by the book".  RemoteWakeupConnected was
+        * saved if boot firmware (BIOS/SMM/...) told us it's connected,
+        * or if bus glue did the same (e.g. for PCI add-in cards with
+        * PCI PM support).
+        */
+       ohci_dbg (ohci, "resetting from state '%s', control = 0x%x\n",
+                       hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
+                       ohci_readl (ohci, &ohci->regs->control));
+       if ((ohci->hc_control & OHCI_CTRL_RWC) != 0
+                       && !device_may_wakeup(hcd->self.controller))
+               device_init_wakeup(hcd->self.controller, 1);
+
+       switch (ohci->hc_control & OHCI_CTRL_HCFS) {
+       case OHCI_USB_OPER:
+               temp = 0;
+               break;
+       case OHCI_USB_SUSPEND:
+       case OHCI_USB_RESUME:
+               ohci->hc_control &= OHCI_CTRL_RWC;
+               ohci->hc_control |= OHCI_USB_RESUME;
+               temp = 10 /* msec wait */;
+               break;
+       // case OHCI_USB_RESET:
+       default:
+               ohci->hc_control &= OHCI_CTRL_RWC;
+               ohci->hc_control |= OHCI_USB_RESET;
+               temp = 50 /* msec wait */;
+               break;
+       }
+       ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+       // flush the writes
+       (void) ohci_readl (ohci, &ohci->regs->control);
+       msleep(temp);
+       temp = roothub_a (ohci);
+       if (!(temp & RH_A_NPS)) {
                /* power down each port */
-               for (temp = 0; temp < ports; temp++)
-                       writel (RH_PS_LSDA,
+               for (temp = 0; temp < ohci->num_ports; temp++)
+                       ohci_writel (ohci, RH_PS_LSDA,
                                &ohci->regs->roothub.portstatus [temp]);
        }
-       // flush those pci writes
-       (void) ohci_readl (&ohci->regs->control);
-       msleep (50);
+       // flush those writes
+       (void) ohci_readl (ohci, &ohci->regs->control);
+       memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
+
+       /* 2msec timelimit here means no irqs/preempt */
+       spin_lock_irq (&ohci->lock);
 
+retry:
        /* HC Reset requires max 10 us delay */
-       writel (OHCI_HCR,  &ohci->regs->cmdstatus);
+       ohci_writel (ohci, OHCI_HCR,  &ohci->regs->cmdstatus);
        temp = 30;      /* ... allow extra time */
-       while ((ohci_readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
+       while ((ohci_readl (ohci, &ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
                if (--temp == 0) {
+                       spin_unlock_irq (&ohci->lock);
                        ohci_err (ohci, "USB HC reset timed out!\n");
                        return -1;
                }
@@ -476,113 +544,89 @@ static int hc_reset (struct ohci_hcd *ohci)
         * ... but some hardware won't init fmInterval "by the book"
         * (SiS, OPTi ...), so reset again instead.  SiS doesn't need
         * this if we write fmInterval after we're OPERATIONAL.
+        * Unclear about ALi, ServerWorks, and others ... this could
+        * easily be a longstanding bug in chip init on Linux.
         */
-       writel (ohci->hc_control, &ohci->regs->control);
-       // flush those pci writes
-       (void) ohci_readl (&ohci->regs->control);
-
-       return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* Start an OHCI controller, set the BUS operational
- * enable interrupts 
- * connect the virtual root hub
- */
-static int hc_start (struct ohci_hcd *ohci)
-{
-       u32                     mask, tmp;
-       struct usb_device       *udev;
-       struct usb_bus          *bus;
-
-       disable (ohci);
+       if (ohci->flags & OHCI_QUIRK_INITRESET) {
+               ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+               // flush those writes
+               (void) ohci_readl (ohci, &ohci->regs->control);
+       }
 
        /* Tell the controller where the control and bulk lists are
         * The lists are empty now. */
-       writel (0, &ohci->regs->ed_controlhead);
-       writel (0, &ohci->regs->ed_bulkhead);
+       ohci_writel (ohci, 0, &ohci->regs->ed_controlhead);
+       ohci_writel (ohci, 0, &ohci->regs->ed_bulkhead);
 
        /* a reset clears this */
-       writel ((u32) ohci->hcca_dma, &ohci->regs->hcca);
+       ohci_writel (ohci, (u32) ohci->hcca_dma, &ohci->regs->hcca);
 
        periodic_reinit (ohci);
 
        /* some OHCI implementations are finicky about how they init.
         * bogus values here mean not even enumeration could work.
         */
-       if ((ohci_readl (&ohci->regs->fminterval) & 0x3fff0000) == 0
-                       || !ohci_readl (&ohci->regs->periodicstart)) {
-               ohci_err (ohci, "init err\n");
+       if ((ohci_readl (ohci, &ohci->regs->fminterval) & 0x3fff0000) == 0
+                       || !ohci_readl (ohci, &ohci->regs->periodicstart)) {
+               if (!(ohci->flags & OHCI_QUIRK_INITRESET)) {
+                       ohci->flags |= OHCI_QUIRK_INITRESET;
+                       ohci_dbg (ohci, "enabling initreset quirk\n");
+                       goto retry;
+               }
+               spin_unlock_irq (&ohci->lock);
+               ohci_err (ohci, "init err (%08x %04x)\n",
+                       ohci_readl (ohci, &ohci->regs->fminterval),
+                       ohci_readl (ohci, &ohci->regs->periodicstart));
                return -EOVERFLOW;
        }
 
-       /* start controller operations */
+       /* use rhsc irqs after khubd is fully initialized */
+       hcd->poll_rh = 1;
+       hcd->uses_new_polling = 1;
+
+       /* start controller operations */
        ohci->hc_control &= OHCI_CTRL_RWC;
-       ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
-       writel (ohci->hc_control, &ohci->regs->control);
-       ohci->hcd.state = USB_STATE_RUNNING;
+       ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
+       ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+       hcd->state = HC_STATE_RUNNING;
 
        /* wake on ConnectStatusChange, matching external hubs */
-       writel (RH_HS_DRWE, &ohci->regs->roothub.status);
+       ohci_writel (ohci, RH_HS_DRWE, &ohci->regs->roothub.status);
 
        /* Choose the interrupts we care about now, others later on demand */
        mask = OHCI_INTR_INIT;
-       writel (mask, &ohci->regs->intrstatus);
-       writel (mask, &ohci->regs->intrenable);
+       ohci_writel (ohci, ~0, &ohci->regs->intrstatus);
+       ohci_writel (ohci, mask, &ohci->regs->intrenable);
 
        /* handle root hub init quirks ... */
-       tmp = roothub_a (ohci);
-       tmp &= ~(RH_A_PSM | RH_A_OCPM);
+       temp = roothub_a (ohci);
+       temp &= ~(RH_A_PSM | RH_A_OCPM);
        if (ohci->flags & OHCI_QUIRK_SUPERIO) {
                /* NSC 87560 and maybe others */
-               tmp |= RH_A_NOCP;
-               tmp &= ~(RH_A_POTPGT | RH_A_NPS);
-       } else if (power_switching) {
-               /* act like most external hubs:  use per-port power
-                * switching and overcurrent reporting.
-                */
-               tmp &= ~(RH_A_NPS | RH_A_NOCP);
-               tmp |= RH_A_PSM | RH_A_OCPM;
-       } else {
+               temp |= RH_A_NOCP;
+               temp &= ~(RH_A_POTPGT | RH_A_NPS);
+               ohci_writel (ohci, temp, &ohci->regs->roothub.a);
+       } else if ((ohci->flags & OHCI_QUIRK_AMD756) || distrust_firmware) {
                /* hub power always on; required for AMD-756 and some
                 * Mac platforms.  ganged overcurrent reporting, if any.
                 */
-               tmp |= RH_A_NPS;
+               temp |= RH_A_NPS;
+               ohci_writel (ohci, temp, &ohci->regs->roothub.a);
        }
-       writel (tmp, &ohci->regs->roothub.a);
-       writel (RH_HS_LPSC, &ohci->regs->roothub.status);
-       writel (power_switching ? RH_B_PPCM : 0, &ohci->regs->roothub.b);
-       // flush those pci writes
-       (void) ohci_readl (&ohci->regs->control);
+       ohci_writel (ohci, RH_HS_LPSC, &ohci->regs->roothub.status);
+       ohci_writel (ohci, (temp & RH_A_NPS) ? 0 : RH_B_PPCM,
+                                               &ohci->regs->roothub.b);
+       // flush those writes
+       (void) ohci_readl (ohci, &ohci->regs->control);
 
-       // POTPGT delay is bits 24-31, in 2 ms units.
-       mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
-       bus = hcd_to_bus (&ohci->hcd);
+       ohci->next_statechange = jiffies + STATECHANGE_DELAY;
+       spin_unlock_irq (&ohci->lock);
 
-       if (bus->root_hub) {
-               ohci->hcd.state = USB_STATE_RUNNING;
-               return 0;
-       }
-       /* connect the virtual root hub */
-       udev = usb_alloc_dev (NULL, bus, 0);
-       ohci->hcd.state = USB_STATE_RUNNING;
-       if (!udev) {
-               disable (ohci);
-               ohci->hc_control &= ~OHCI_CTRL_HCFS;
-               writel (ohci->hc_control, &ohci->regs->control);
-               return -ENOMEM;
-       }
+       // POTPGT delay is bits 24-31, in 2 ms units.
+       mdelay ((temp >> 23) & 0x1fe);
+       hcd->state = HC_STATE_RUNNING;
 
-       udev->speed = USB_SPEED_FULL;
-       if (hcd_register_root (udev, &ohci->hcd) != 0) {
-               usb_put_dev (udev);
-               disable (ohci);
-               ohci->hc_control &= ~OHCI_CTRL_HCFS;
-               writel (ohci->hc_control, &ohci->regs->control);
-               return -ENODEV;
-       }
+       ohci_dump (ohci, 1);
 
        return 0;
 }
@@ -591,7 +635,7 @@ static int hc_start (struct ohci_hcd *ohci)
 
 /* an interrupt happens */
 
-static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
+static irqreturn_t ohci_irq (struct usb_hcd *hcd)
 {
        struct ohci_hcd         *ohci = hcd_to_ohci (hcd);
        struct ohci_regs __iomem *regs = ohci->regs;
@@ -600,19 +644,20 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
        /* we can eliminate a (slow) ohci_readl()
           if _only_ WDH caused this irq */
        if ((ohci->hcca->done_head != 0)
-                       && ! (le32_to_cpup (&ohci->hcca->done_head) & 0x01)) {
+                       && ! (hc32_to_cpup (ohci, &ohci->hcca->done_head)
+                               & 0x01)) {
                ints =  OHCI_INTR_WDH;
 
        /* cardbus/... hardware gone before remove() */
-       } else if ((ints = ohci_readl (&regs->intrstatus)) == ~(u32)0) {
+       } else if ((ints = ohci_readl (ohci, &regs->intrstatus)) == ~(u32)0) {
                disable (ohci);
                ohci_dbg (ohci, "device removed!\n");
                return IRQ_HANDLED;
 
        /* interrupt for some other device? */
-       } else if ((ints &= ohci_readl (&regs->intrenable)) == 0) {
-               return IRQ_NONE;
-       } 
+       } else if ((ints &= ohci_readl (ohci, &regs->intrenable)) == 0) {
+               return IRQ_NOTMINE;
+       }
 
        if (ints & OHCI_INTR_UE) {
                disable (ohci);
@@ -620,24 +665,53 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
                // e.g. due to PCI Master/Target Abort
 
                ohci_dump (ohci, 1);
-               hc_reset (ohci);
+               ohci_usb_reset (ohci);
        }
 
-       if (ints & OHCI_INTR_RD) {
-               ohci_vdbg (ohci, "resume detect\n");
-               schedule_work(&ohci->rh_resume);
+       if (ints & OHCI_INTR_RHSC) {
+               ohci_vdbg(ohci, "rhsc\n");
+               ohci->next_statechange = jiffies + STATECHANGE_DELAY;
+               ohci_writel(ohci, OHCI_INTR_RD | OHCI_INTR_RHSC,
+                               &regs->intrstatus);
+
+               /* NOTE: Vendors didn't always make the same implementation
+                * choices for RHSC.  Many followed the spec; RHSC triggers
+                * on an edge, like setting and maybe clearing a port status
+                * change bit.  With others it's level-triggered, active
+                * until khubd clears all the port status change bits.  We'll
+                * always disable it here and rely on polling until khubd
+                * re-enables it.
+                */
+               ohci_writel(ohci, OHCI_INTR_RHSC, &regs->intrdisable);
+               usb_hcd_poll_rh_status(hcd);
+       }
+
+       /* For connect and disconnect events, we expect the controller
+        * to turn on RHSC along with RD.  But for remote wakeup events
+        * this might not happen.
+        */
+       else if (ints & OHCI_INTR_RD) {
+               ohci_vdbg(ohci, "resume detect\n");
+               ohci_writel(ohci, OHCI_INTR_RD, &regs->intrstatus);
+               hcd->poll_rh = 1;
+               if (ohci->autostop) {
+                       spin_lock (&ohci->lock);
+                       ohci_rh_resume (ohci);
+                       spin_unlock (&ohci->lock);
+               } else
+                       usb_hcd_resume_root_hub(hcd);
        }
 
        if (ints & OHCI_INTR_WDH) {
-               if (HCD_IS_RUNNING(hcd->state))
-                       writel (OHCI_INTR_WDH, &regs->intrdisable);     
+               if (HC_IS_RUNNING(hcd->state))
+                       ohci_writel (ohci, OHCI_INTR_WDH, &regs->intrdisable);
                spin_lock (&ohci->lock);
-               dl_done_list (ohci, ptregs);
+               dl_done_list (ohci);
                spin_unlock (&ohci->lock);
-               if (HCD_IS_RUNNING(hcd->state))
-                       writel (OHCI_INTR_WDH, &regs->intrenable); 
+               if (HC_IS_RUNNING(hcd->state))
+                       ohci_writel (ohci, OHCI_INTR_WDH, &regs->intrenable);
        }
-  
+
        /* could track INTR_SO to reduce available PCI/... bandwidth */
 
        /* handle any pending URB/ED unlinks, leaving INTR_SF enabled
@@ -645,18 +719,17 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
         */
        spin_lock (&ohci->lock);
        if (ohci->ed_rm_list)
-               finish_unlinks (ohci, OHCI_FRAME_NO(ohci->hcca),
-                               ptregs);
+               finish_unlinks (ohci, ohci_frame_no(ohci));
        if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list
-                       && HCD_IS_RUNNING(ohci->hcd.state))
-               writel (OHCI_INTR_SF, &regs->intrdisable);      
+                       && HC_IS_RUNNING(hcd->state))
+               ohci_writel (ohci, OHCI_INTR_SF, &regs->intrdisable);
        spin_unlock (&ohci->lock);
 
-       if (HCD_IS_RUNNING(ohci->hcd.state)) {
-               writel (ints, &regs->intrstatus);
-               writel (OHCI_INTR_MIE, &regs->intrenable);      
-               // flush those pci writes
-               (void) ohci_readl (&ohci->regs->control);
+       if (HC_IS_RUNNING(hcd->state)) {
+               ohci_writel (ohci, ints, &regs->intrstatus);
+               ohci_writel (ohci, OHCI_INTR_MIE, &regs->intrenable);
+               // flush those writes
+               (void) ohci_readl (ohci, &ohci->regs->control);
        }
 
        return IRQ_HANDLED;
@@ -665,25 +738,26 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
 /*-------------------------------------------------------------------------*/
 
 static void ohci_stop (struct usb_hcd *hcd)
-{      
+{
        struct ohci_hcd         *ohci = hcd_to_ohci (hcd);
 
        ohci_dbg (ohci, "stop %s controller (state 0x%02x)\n",
                hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
-               ohci->hcd.state);
+               hcd->state);
        ohci_dump (ohci, 1);
 
        flush_scheduled_work();
-       if (HCD_IS_RUNNING(ohci->hcd.state))
-               hc_reset (ohci);
-       else
-               writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
-       
+
+       ohci_usb_reset (ohci);
+       ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
+       free_irq(hcd->irq, hcd);
+       hcd->irq = -1;
+
        remove_debug_files (ohci);
        ohci_mem_cleanup (ohci);
        if (ohci->hcca) {
-               dma_free_coherent (ohci->hcd.self.controller, 
-                               sizeof *ohci->hcca, 
+               dma_free_coherent (hcd->self.controller,
+                               sizeof *ohci->hcca,
                                ohci->hcca, ohci->hcca_dma);
                ohci->hcca = NULL;
                ohci->hcca_dma = 0;
@@ -694,21 +768,9 @@ static void ohci_stop (struct usb_hcd *hcd)
 
 /* must not be called from interrupt context */
 
-#if    defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
+#ifdef CONFIG_PM
 
-static void mark_children_gone (struct usb_device *dev)
-{
-       unsigned i;
-
-       for (i = 0; i < dev->maxchild; i++) {
-               if (dev->children [i] == 0)
-                       continue;
-               dev->children [i]->state = USB_STATE_NOTATTACHED;
-               mark_children_gone (dev->children [i]);
-       }
-}
-
-static int hc_restart (struct ohci_hcd *ohci)
+static int ohci_restart (struct ohci_hcd *ohci)
 {
        int temp;
        int i;
@@ -718,10 +780,10 @@ static int hc_restart (struct ohci_hcd *ohci)
         * recycle any "live" eds/tds (and urbs) right away.
         * later, khubd disconnect processing will recycle the other state,
         * (either as disconnect/reconnect, or maybe someday as a reset).
-        */ 
+        */
        spin_lock_irq(&ohci->lock);
        disable (ohci);
-       mark_children_gone (ohci->hcd.self.root_hub);
+       usb_root_hub_lost_power(ohci_to_hcd(ohci)->self.root_hub);
        if (!list_empty (&ohci->pending))
                ohci_dbg(ohci, "abort schedule...\n");
        list_for_each_entry (priv, &ohci->pending, pending) {
@@ -731,7 +793,7 @@ static int hc_restart (struct ohci_hcd *ohci)
                switch (ed->state) {
                case ED_OPER:
                        ed->state = ED_UNLINK;
-                       ed->hwINFO |= ED_DEQUEUE;
+                       ed->hwINFO |= cpu_to_hc32(ohci, ED_DEQUEUE);
                        ed_deschedule (ohci, ed);
 
                        ed->ed_next = ohci->ed_rm_list;
@@ -749,7 +811,7 @@ static int hc_restart (struct ohci_hcd *ohci)
                urb->status = -ESHUTDOWN;
                spin_unlock (&urb->lock);
        }
-       finish_unlinks (ohci, 0, NULL);
+       finish_unlinks (ohci, 0);
        spin_unlock_irq(&ohci->lock);
 
        /* paranoia, in case that didn't work: */
@@ -757,15 +819,15 @@ static int hc_restart (struct ohci_hcd *ohci)
        /* empty the interrupt branches */
        for (i = 0; i < NUM_INTS; i++) ohci->load [i] = 0;
        for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0;
-       
+
        /* no EDs to remove */
        ohci->ed_rm_list = NULL;
 
-       /* empty control and bulk lists */       
+       /* empty control and bulk lists */
        ohci->ed_controltail = NULL;
        ohci->ed_bulktail    = NULL;
 
-       if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) {
+       if ((temp = ohci_run (ohci)) < 0) {
                ohci_err (ohci, "can't restart, %d\n", temp);
                return temp;
        } else {
@@ -773,14 +835,11 @@ static int hc_restart (struct ohci_hcd *ohci)
                 * and that if we try to turn them back on the root hub
                 * will respond to CSC processing.
                 */
-               i = roothub_a (ohci) & RH_A_NDP;
+               i = ohci->num_ports;
                while (i--)
-                       writel (RH_PS_PSS,
-                               &ohci->regs->roothub.portstatus [temp]);
-               ohci->hcd.self.root_hub->dev.power.power_state = 0;
-               ohci->hcd.state = USB_STATE_RUNNING;
+                       ohci_writel (ohci, RH_PS_PSS,
+                               &ohci->regs->roothub.portstatus [i]);
                ohci_dbg (ohci, "restart complete\n");
-               ohci_dump (ohci, 1);
        }
        return 0;
 }
@@ -802,6 +861,10 @@ MODULE_LICENSE ("GPL");
 #include "ohci-sa1111.c"
 #endif
 
+#ifdef CONFIG_ARCH_S3C2410
+#include "ohci-s3c2410.c"
+#endif
+
 #ifdef CONFIG_ARCH_OMAP
 #include "ohci-omap.c"
 #endif
@@ -810,10 +873,45 @@ MODULE_LICENSE ("GPL");
 #include "ohci-lh7a404.c"
 #endif
 
+#ifdef CONFIG_PXA27x
+#include "ohci-pxa27x.c"
+#endif
+
+#ifdef CONFIG_ARCH_EP93XX
+#include "ohci-ep93xx.c"
+#endif
+
+#ifdef CONFIG_SOC_AU1X00
+#include "ohci-au1xxx.c"
+#endif
+
+#ifdef CONFIG_PNX8550
+#include "ohci-pnx8550.c"
+#endif
+
+#ifdef CONFIG_USB_OHCI_HCD_PPC_SOC
+#include "ohci-ppc-soc.c"
+#endif
+
+#ifdef CONFIG_ARCH_AT91
+#include "ohci-at91.c"
+#endif
+
+#ifdef CONFIG_ARCH_PNX4008
+#include "ohci-pnx4008.c"
+#endif
+
 #if !(defined(CONFIG_PCI) \
       || defined(CONFIG_SA1111) \
+      || defined(CONFIG_ARCH_S3C2410) \
       || defined(CONFIG_ARCH_OMAP) \
       || defined (CONFIG_ARCH_LH7A404) \
+      || defined (CONFIG_PXA27x) \
+      || defined (CONFIG_ARCH_EP93XX) \
+      || defined (CONFIG_SOC_AU1X00) \
+      || defined (CONFIG_USB_OHCI_HCD_PPC_SOC) \
+      || defined (CONFIG_ARCH_AT91) \
+      || defined (CONFIG_ARCH_PNX4008) \
        )
 #error "missing bus glue for ohci-hcd"
 #endif