diff -X dontdiff.txt -Naur linux-2.5.20/drivers/usb/host/ohci-hcd.c 
linux-2.5.20-usbwork/drivers/usb/host/ohci-hcd.c
--- linux-2.5.20/drivers/usb/host/ohci-hcd.c    Sun Jun  2 18:44:41 2002
+++ linux-2.5.20-usbwork/drivers/usb/host/ohci-hcd.c    Wed Jun  5 18:27:07 2002
@@ -12,6 +12,9 @@
  * 
  * History:
  * 
+ * 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.
  *
@@ -75,9 +78,9 @@
 #include <linux/interrupt.h>  /* for in_interrupt () */
 
 #ifdef CONFIG_USB_DEBUG
-       #define DEBUG
+#      define DEBUG
 #else
-       #undef DEBUG
+#      undef DEBUG
 #endif
 
 #include <linux/usb.h>
@@ -106,7 +109,7 @@
  *     - lots more testing!!
  */
 
-#define DRIVER_VERSION "$Revision: 1.9 $"
+#define DRIVER_VERSION "2002-Jun-01"
 #define DRIVER_AUTHOR "Roman Weissgaerber <[EMAIL PROTECTED]>, David Brownell"
 #define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
 
@@ -287,7 +290,7 @@
                }
                        
                urb_priv->state = URB_DEL; 
-               ed_unlink (urb->dev, urb_priv->ed);
+               start_urb_unlink (ohci, urb_priv->ed);
                spin_unlock_irqrestore (&ohci->lock, flags);
        } else {
                /*
@@ -408,7 +411,14 @@
        __u32                   mask;
        unsigned int            fminterval;
        struct usb_device       *udev;
-       
+       struct device *parent_dev;
+
+#ifdef CONFIG_PCI
+       parent_dev = &ohci->hcd.pdev->dev;
+#else
+       parent_dev = 0;
+#endif
+
        spin_lock_init (&ohci->lock);
        ohci->disabled = 1;
        ohci->sleeping = 0;
@@ -459,7 +469,7 @@
 
        usb_connect (udev);
        udev->speed = USB_SPEED_FULL;
-       if (usb_register_root_hub (udev, &ohci->hcd.pdev->dev) != 0) {
+       if (usb_register_root_hub (udev, parent_dev) != 0) {
                usb_free_dev (udev); 
                ohci->disabled = 1;
 // FIXME cleanup
@@ -508,16 +518,15 @@
   
        /* could track INTR_SO to reduce available PCI/... bandwidth */
 
-       // FIXME:  this assumes SOF (1/ms) interrupts don't get lost...
-       if (ints & OHCI_INTR_SF) { 
-               unsigned int frame = le16_to_cpu (ohci->hcca->frame_no) & 1;
+       /* handle any pending URB/ED unlinks, leaving INTR_SF enabled
+        * when there's still unlinking to be done (next frame).
+        */
+       spin_lock (&ohci->lock);
+       if (ohci->ed_rm_list)
+               finish_unlinks (ohci, le16_to_cpu (ohci->hcca->frame_no));
+       if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list)
                writel (OHCI_INTR_SF, &regs->intrdisable);      
-               if (ohci->ed_rm_list [!frame] != NULL) {
-                       dl_del_list (ohci, !frame);
-               }
-               if (ohci->ed_rm_list [frame] != NULL)
-                       writel (OHCI_INTR_SF, &regs->intrenable);       
-       }
+       spin_unlock (&ohci->lock);
 
        writel (ints, &regs->intrstatus);
        writel (OHCI_INTR_MIE, &regs->intrenable);      
@@ -543,7 +552,7 @@
        
        ohci_mem_cleanup (ohci);
 
-#ifdef CONFIG_PCI
+#if defined(CONFIG_PCI) || defined(CONFIG_SA1111)
        pci_free_consistent (ohci->hcd.pdev, sizeof *ohci->hcca,
                ohci->hcca, ohci->hcca_dma);
 #endif
@@ -557,13 +566,15 @@
        struct ohci_hcd *ohci = hcd_to_ohci (hcd);
        int             ret;
 
-#ifdef CONFIG_PCI
+dbg("enter: " __FUNCTION__);
+#if defined(CONFIG_PCI) || defined(CONFIG_SA1111)
        if (hcd->pdev) {
                ohci->hcca = pci_alloc_consistent (hcd->pdev,
                                sizeof *ohci->hcca, &ohci->hcca_dma);
                if (!ohci->hcca)
                        return -ENOMEM;
 
+#      if defined(CONFIG_PCI)
                /* AMD 756, for most chips (early revs), corrupts register
                 * values on read ... so enable the vendor workaround.
                 */
@@ -583,6 +594,7 @@
                        info ("%s: WARNING: OPTi workarounds unavailable",
                                hcd->self.bus_name);
                }
+#      endif
        }
 #else
 #      error "where's hcca coming from?"
@@ -646,11 +658,11 @@
        if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF)
                mdelay (1);
                
- #ifdef CONFIG_PMAC_PBOOK
+#ifdef CONFIG_PMAC_PBOOK
        if (_machine == _MACH_Pmac)
                disable_irq (hcd->pdev->irq);
        /* else, 2.4 assumes shared irqs -- don't disable */
- #endif
+#endif
 
        /* Enable remote wakeup */
        writel (readl (&ohci->regs->intrenable) | OHCI_INTR_RD,
@@ -681,9 +693,11 @@
         * memory during sleep. We disable its bus master bit during
         * suspend
         */
+#ifdef CONFIG_PCI
        pci_read_config_word (hcd->pdev, PCI_COMMAND, &cmd);
        cmd &= ~PCI_COMMAND_MASTER;
        pci_write_config_word (hcd->pdev, PCI_COMMAND, cmd);
+#endif
 #ifdef CONFIG_PMAC_PBOOK
        {
                struct device_node      *of_node;
@@ -719,8 +733,7 @@
        for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0;
        
        /* no EDs to remove */
-       ohci->ed_rm_list [0] = NULL;
-       ohci->ed_rm_list [1] = NULL;
+       ohci->ed_rm_list = NULL;
 
        /* empty control and bulk lists */       
        ohci->ed_isotail     = NULL;
@@ -761,8 +774,10 @@
        ohci_dump_status (ohci);
 #endif
 
+#ifdef CONFIG_PCI
        /* Re-enable bus mastering */
        pci_set_master (ohci->hcd.pdev);
+#endif
        
        switch (temp) {
 
@@ -802,7 +817,7 @@
                ohci->disabled = 0;
                ohci->sleeping = 0;
                ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
-               if (!ohci->ed_rm_list [0] && !ohci->ed_rm_list [1]) {
+               if (!ohci->ed_rm_list) {
                        if (ohci->ed_controltail)
                                ohci->hc_control |= OHCI_CTRL_CLE;
                        if (ohci->ed_bulktail)
@@ -820,10 +835,10 @@
                (void) readl (&ohci->regs->intrdisable);
                spin_unlock_irqrestore (&ohci->lock, flags);
 
- #ifdef CONFIG_PMAC_PBOOK
+#ifdef CONFIG_PMAC_PBOOK
                if (_machine == _MACH_Pmac)
                        enable_irq (hcd->pdev->irq);
- #endif
+#endif
                if (ohci->hcca->done_head)
                        dl_done_list (ohci, dl_reverse_done_list (ohci));
                writel (OHCI_INTR_WDH, &ohci->regs->intrenable); 
@@ -848,113 +863,17 @@
 
 static const char      hcd_name [] = "ohci-hcd";
 
-static const struct hc_driver ohci_driver = {
-       description:            hcd_name,
-
-       /*
-        * generic hardware linkage
-        */
-       irq:                    ohci_irq,
-       flags:                  HCD_MEMORY | HCD_USB11,
-
-       /*
-        * basic lifecycle operations
-        */
-       start:                  ohci_start,
-#ifdef CONFIG_PM
-       suspend:                ohci_suspend,
-       resume:                 ohci_resume,
-#endif
-       stop:                   ohci_stop,
-
-       /*
-        * memory lifecycle (except per-request)
-        */
-       hcd_alloc:              ohci_hcd_alloc,
-       hcd_free:               ohci_hcd_free,
-
-       /*
-        * managing i/o requests and associated device resources
-        */
-       urb_enqueue:            ohci_urb_enqueue,
-       urb_dequeue:            ohci_urb_dequeue,
-       free_config:            ohci_free_config,
-
-       /*
-        * scheduling support
-        */
-       get_frame_number:       ohci_get_frame,
-
-       /*
-        * root hub support
-        */
-       hub_status_data:        ohci_hub_status_data,
-       hub_control:            ohci_hub_control,
-};
-
 #define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
 
 MODULE_AUTHOR (DRIVER_AUTHOR);
 MODULE_DESCRIPTION (DRIVER_INFO);
 MODULE_LICENSE ("GPL");
 
-/*-------------------------------------------------------------------------*/
-
 #ifdef CONFIG_PCI
-
-/* There do exist non-PCI implementations of OHCI ...
- * Examples include the SA-1111 (ARM) and some MIPS
- * and related hardware.
- */
-
-static const struct pci_device_id __devinitdata pci_ids [] = { {
-
-       /* handle any USB OHCI controller */
-       class:          (PCI_CLASS_SERIAL_USB << 8) | 0x10,
-       class_mask:     ~0,
-       driver_data:    (unsigned long) &ohci_driver,
-
-       /* no matter who makes it */
-       vendor:         PCI_ANY_ID,
-       device:         PCI_ANY_ID,
-       subvendor:      PCI_ANY_ID,
-       subdevice:      PCI_ANY_ID,
-
-       }, { /* end: all zeroes */ }
-};
-MODULE_DEVICE_TABLE (pci, pci_ids);
-
-/* pci driver glue; this is a "new style" PCI driver module */
-static struct pci_driver ohci_pci_driver = {
-       name:           (char *) hcd_name,
-       id_table:       pci_ids,
-
-       probe:          usb_hcd_pci_probe,
-       remove:         usb_hcd_pci_remove,
-
-#ifdef CONFIG_PM
-       suspend:        usb_hcd_pci_suspend,
-       resume:         usb_hcd_pci_resume,
+#include "ohci-pci.c"
 #endif
-};
 
- 
-static int __init ohci_hcd_init (void) 
-{
-       dbg (DRIVER_INFO);
-       dbg ("block sizes: ed %d td %d",
-               sizeof (struct ed), sizeof (struct td));
-       return pci_module_init (&ohci_pci_driver);
-}
-module_init (ohci_hcd_init);
-
-/*-------------------------------------------------------------------------*/
-
-static void __exit ohci_hcd_cleanup (void) 
-{      
-       pci_unregister_driver (&ohci_pci_driver);
-}
-module_exit (ohci_hcd_cleanup);
-
-#endif /* CONFIG_PCI */
+#ifdef CONFIG_SA1111
+#include "ohci-sa1111.c"
+#endif
 
diff -X dontdiff.txt -Naur linux-2.5.20/drivers/usb/host/ohci.h 
linux-2.5.20-usbwork/drivers/usb/host/ohci.h
--- linux-2.5.20/drivers/usb/host/ohci.h        Sun Jun  2 18:44:53 2002
+++ linux-2.5.20-usbwork/drivers/usb/host/ohci.h        Wed Jun  5 18:24:01 2002
@@ -27,22 +27,24 @@
        __u32                   hwNextED;       /* next ED in list */
 
        /* rest are purely for the driver's use */
-       struct ed               *ed_prev;  
-       __u8                    int_period;
-       __u8                    int_branch;
-       __u8                    int_load; 
-       __u8                    int_interval;
-       __u8                    state;                  // ED_{NEW,UNLINK,OPER}
+       dma_addr_t              dma;            /* addr of ED */
+       struct ed               *ed_prev;       /* for non-interrupt EDs */
+
+       u8                      type;           /* PIPE_{BULK,...} */
+       u8                      interval;       /* interrupt, isochronous */
+       u8                      int_period;
+       u8                      int_branch;
+       u8                      int_load; 
+       u16                     last_iso;       /* isochronous */
+       u8                      state;          /* ED_{NEW,UNLINK,OPER} */
 #define ED_NEW                 0x00            /* unused, no dummy td */
 #define ED_UNLINK      0x01            /* dummy td, maybe linked to hc */
 #define ED_OPER                0x02            /* dummy td, _is_ linked to hc */
 #define ED_URB_DEL     0x08            /* for unlinking; masked in */
 
-       __u8                    type; 
-       __u16                   last_iso;
+       /* HC may see EDs on rm_list until next frame (frame_no == tick) */
+       u16                     tick;
        struct ed               *ed_rm_list;
-
-       dma_addr_t              dma;                    /* addr of ED */
 } __attribute__ ((aligned(16)));
 
 #define ED_MASK        ((u32)~0x0f)            /* strip hw status in low addr bits */
@@ -335,13 +337,13 @@
        struct ohci_hcca        *hcca;
        dma_addr_t              hcca_dma;
 
-       struct ed               *ed_rm_list [2];        /* to be removed */
+       struct ed               *ed_rm_list;            /* to be removed */
 
        struct ed               *ed_bulktail;           /* last in bulk list */
        struct ed               *ed_controltail;        /* last in ctrl list */
        struct ed               *ed_isotail;            /* last in iso list */
 
-#ifdef CONFIG_PCI
+#if defined(CONFIG_PCI) || defined(CONFIG_SA1111)
        struct pci_pool         *td_cache;
        struct pci_pool         *ed_cache;
        struct hash_list_t      td_hash [TD_HASH_SIZE];
diff -X dontdiff.txt -Naur linux-2.5.20/drivers/usb/host/ohci-hub.c 
linux-2.5.20-usbwork/drivers/usb/host/ohci-hub.c
--- linux-2.5.20/drivers/usb/host/ohci-hub.c    Sun Jun  2 18:44:42 2002
+++ linux-2.5.20-usbwork/drivers/usb/host/ohci-hub.c    Wed Jun  5 17:29:57 2002
@@ -63,7 +63,7 @@
 
 /* build "status change" packet (one or two bytes) from HC registers */
 
-static int
+int
 ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
 {
        struct ohci_hcd *ohci = hcd_to_ohci (hcd);
@@ -145,7 +145,7 @@
 
 /*-------------------------------------------------------------------------*/
 
-static int ohci_hub_control (
+int ohci_hub_control (
        struct usb_hcd  *hcd,
        u16             typeReq,
        u16             wValue,
diff -X dontdiff.txt -Naur linux-2.5.20/drivers/usb/host/ohci-mem.c 
linux-2.5.20-usbwork/drivers/usb/host/ohci-mem.c
--- linux-2.5.20/drivers/usb/host/ohci-mem.c    Sun Jun  2 18:44:51 2002
+++ linux-2.5.20-usbwork/drivers/usb/host/ohci-mem.c    Wed Jun  5 17:29:57 2002
@@ -23,7 +23,7 @@
 
 /*-------------------------------------------------------------------------*/
 
-static struct usb_hcd *ohci_hcd_alloc (void)
+struct usb_hcd *ohci_hcd_alloc (void)
 {
        struct ohci_hcd *ohci;
 
@@ -35,14 +35,14 @@
        return 0;
 }
 
-static void ohci_hcd_free (struct usb_hcd *hcd)
+void ohci_hcd_free (struct usb_hcd *hcd)
 {
        kfree (hcd_to_ohci (hcd));
 }
 
 /*-------------------------------------------------------------------------*/
 
-#ifndef CONFIG_PCI
+#if !(defined(CONFIG_PCI) || defined(CONFIG_SA1111))
 #      error "usb-ohci currently requires PCI-based controllers"
        /* to support non-PCI OHCIs, you need custom bus/mem/... glue */
 #endif
diff -X dontdiff.txt -Naur linux-2.5.20/drivers/usb/host/ohci-pci.c 
linux-2.5.20-usbwork/drivers/usb/host/ohci-pci.c
--- linux-2.5.20/drivers/usb/host/ohci-pci.c    Wed Dec 31 16:00:00 1969
+++ linux-2.5.20-usbwork/drivers/usb/host/ohci-pci.c    Wed Jun  5 18:42:45 2002
@@ -0,0 +1,113 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <[EMAIL PROTECTED]>
+ * (C) Copyright 2000-2002 David Brownell <[EMAIL PROTECTED]>
+ * 
+ * [ Initialisation is based on Linus'  ]
+ * [ uhci code and gregs ohci fragments ]
+ * [ (C) Copyright 1999 Linus Torvalds  ]
+ * [ (C) Copyright 1999 Gregory P. Smith]
+ * 
+ * PCI Bus Glue
+ *
+ * This file is licenced under the GPL.
+ */
+ 
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ohci_pci_hc_driver = {
+       description:            hcd_name,
+
+       /*
+        * generic hardware linkage
+        */
+       irq:                    ohci_irq,
+       flags:                  HCD_MEMORY | HCD_USB11,
+
+       /*
+        * basic lifecycle operations
+        */
+       start:                  ohci_start,
+#ifdef CONFIG_PM
+       suspend:                ohci_suspend,
+       resume:                 ohci_resume,
+#endif
+       stop:                   ohci_stop,
+
+       /*
+        * memory lifecycle (except per-request)
+        */
+       hcd_alloc:              ohci_hcd_alloc,
+       hcd_free:               ohci_hcd_free,
+
+       /*
+        * managing i/o requests and associated device resources
+        */
+       urb_enqueue:            ohci_urb_enqueue,
+       urb_dequeue:            ohci_urb_dequeue,
+       free_config:            ohci_free_config,
+
+       /*
+        * scheduling support
+        */
+       get_frame_number:       ohci_get_frame,
+
+       /*
+        * root hub support
+        */
+       hub_status_data:        ohci_hub_status_data,
+       hub_control:            ohci_hub_control,
+};
+
+/*-------------------------------------------------------------------------*/
+
+
+static const struct pci_device_id __devinitdata pci_ids [] = { {
+
+       /* handle any USB OHCI controller */
+       class:          (PCI_CLASS_SERIAL_USB << 8) | 0x10,
+       class_mask:     ~0,
+       driver_data:    (unsigned long) &ohci_pci_hc_driver,
+
+       /* no matter who makes it */
+       vendor:         PCI_ANY_ID,
+       device:         PCI_ANY_ID,
+       subvendor:      PCI_ANY_ID,
+       subdevice:      PCI_ANY_ID,
+
+       }, { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE (pci, pci_ids);
+
+/* pci driver glue; this is a "new style" PCI driver module */
+static struct pci_driver ohci_pci_driver = {
+       name:           (char *) hcd_name,
+       id_table:       pci_ids,
+
+       probe:          usb_hcd_pci_probe,
+       remove:         usb_hcd_pci_remove,
+
+#ifdef CONFIG_PM
+       suspend:        usb_hcd_pci_suspend,
+       resume:         usb_hcd_pci_resume,
+#endif
+};
+/*-------------------------------------------------------------------------*/
+ 
+static int __init ohci_hcd_pci_init (void) 
+{
+       dbg (DRIVER_INFO " (PCI)");
+       dbg ("block sizes: ed %d td %d",
+               sizeof (struct ed), sizeof (struct td));
+       return pci_module_init (&ohci_pci_driver);
+}
+module_init (ohci_hcd_pci_init);
+
+/*-------------------------------------------------------------------------*/
+
+static void __exit ohci_hcd_pci_cleanup (void) 
+{      
+       pci_unregister_driver (&ohci_pci_driver);
+}
+module_exit (ohci_hcd_pci_cleanup);
diff -X dontdiff.txt -Naur linux-2.5.20/drivers/usb/host/ohci-q.c 
linux-2.5.20-usbwork/drivers/usb/host/ohci-q.c
--- linux-2.5.20/drivers/usb/host/ohci-q.c      Sun Jun  2 18:44:53 2002
+++ linux-2.5.20-usbwork/drivers/usb/host/ohci-q.c      Wed Jun  5 17:29:57 2002
@@ -15,7 +15,7 @@
        if (last >= 0) {
                int             i;
                struct td       *td = urb_priv->td [0];
-#ifdef CONFIG_PCI
+#if defined(CONFIG_PCI) || defined(CONFIG_SA1111)
                int             len = td->urb->transfer_buffer_length;
                int             dir = usb_pipeout (td->urb->pipe)
                                        ? PCI_DMA_TODEVICE
@@ -90,7 +90,7 @@
        urb_priv_t      *urb_priv = urb->hcpriv;
        unsigned long   flags;
 
-#ifdef CONFIG_PCI
+#if defined(CONFIG_PCI) || defined(CONFIG_SA1111)
 // FIXME rewrite this resubmit path.  use pci_dma_sync_single()
 // and requeue more cheaply, and only if needed.
 // Better yet ... abolish the notion of automagic resubmission.
@@ -208,8 +208,7 @@
                }
                ed->ed_prev = ohci->ed_controltail;
                if (!ohci->ed_controltail
-                               && !ohci->ed_rm_list [0]
-                               && !ohci->ed_rm_list [1]
+                               && !ohci->ed_rm_list
                                && !ohci->sleeping
                                ) {
                        ohci->hc_control |= OHCI_CTRL_CLE;
@@ -227,8 +226,7 @@
                }
                ed->ed_prev = ohci->ed_bulktail;
                if (!ohci->ed_bulktail
-                               && !ohci->ed_rm_list [0]
-                               && !ohci->ed_rm_list [1]
+                               && !ohci->ed_rm_list
                                && !ohci->sleeping
                                ) {
                        ohci->hc_control |= OHCI_CTRL_BLE;
@@ -240,16 +238,16 @@
        case PIPE_INTERRUPT:
                load = ed->int_load;
                interval = ep_2_n_interval (ed->int_period);
-               ed->int_interval = interval;
+               ed->interval = interval;
                int_branch = ep_int_balance (ohci, interval, load);
                ed->int_branch = int_branch;
 
                for (i = 0; i < ep_rev (6, interval); i += inter) {
                        inter = 1;
                        for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i) + 
int_branch]); 
-                                (*ed_p != 0) && ((dma_to_ed (ohci, le32_to_cpup 
(ed_p)))->int_interval >= interval); 
+                                (*ed_p != 0) && ((dma_to_ed (ohci, le32_to_cpup 
+(ed_p)))->interval >= interval); 
                                ed_p = & ((dma_to_ed (ohci, le32_to_cpup 
(ed_p)))->hwNextED)) 
-                                       inter = ep_rev (6, (dma_to_ed (ohci, 
le32_to_cpup (ed_p)))->int_interval);
+                                       inter = ep_rev (6, (dma_to_ed (ohci, 
+le32_to_cpup (ed_p)))->interval);
                        ed->hwNextED = *ed_p; 
                        *ed_p = cpu_to_le32 (ed->dma);
                }
@@ -260,7 +258,7 @@
 
        case PIPE_ISOCHRONOUS:
                ed->hwNextED = 0;
-               ed->int_interval = 1;
+               ed->interval = 1;
                if (ohci->ed_isotail != NULL) {
                        ohci->ed_isotail->hwNextED = cpu_to_le32 (ed->dma);
                        ed->ed_prev = ohci->ed_isotail;
@@ -270,7 +268,7 @@
                                for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i)]); 
                                        *ed_p != 0; 
                                        ed_p = & ((dma_to_ed (ohci, le32_to_cpup 
(ed_p)))->hwNextED)) 
-                                               inter = ep_rev (6, (dma_to_ed (ohci, 
le32_to_cpup (ed_p)))->int_interval);
+                                               inter = ep_rev (6, (dma_to_ed (ohci, 
+le32_to_cpup (ed_p)))->interval);
                                *ed_p = cpu_to_le32 (ed->dma);  
                        }       
                        ed->ed_prev = NULL;
@@ -311,7 +309,7 @@
  * the link from the ed still points to another operational ed or 0
  * so the HC can eventually finish the processing of the unlinked ed
  */
-static int ep_unlink (struct ohci_hcd *ohci, struct ed *ed) 
+static int start_ed_unlink (struct ohci_hcd *ohci, struct ed *ed) 
 {
        int     i;
 
@@ -357,8 +355,8 @@
                break;
 
        case PIPE_INTERRUPT:
-               periodic_unlink (ohci, ed, ed->int_branch, ed->int_interval);
-               for (i = ed->int_branch; i < NUM_INTS; i += ed->int_interval)
+               periodic_unlink (ohci, ed, ed->int_branch, ed->interval);
+               for (i = ed->int_branch; i < NUM_INTS; i += ed->interval)
                        ohci->ohci_int_load [i] -= ed->int_load;
 #ifdef OHCI_VERBOSE_DEBUG
                ohci_dump_periodic (ohci, "UNLINK_INT");
@@ -384,6 +382,10 @@
 
        /* FIXME ED's "unlink" state is indeterminate;
         * the HC might still be caching it (till SOF).
+        * - use ed_rm_list and finish_unlinks(), adding some state that
+        *   prevents clobbering hw linkage before the appropriate SOF
+        * - a speedup:  when only one urb is queued on the ed, save 1msec
+        *   by making start_urb_unlink() use this routine to deschedule.
         */
        ed->state = ED_UNLINK;
        return 0;
@@ -478,11 +480,8 @@
  * put the ep on the rm_list and stop the bulk or ctrl list 
  * real work is done at the next start frame (SF) hardware interrupt
  */
-static void ed_unlink (struct usb_device *usb_dev, struct ed *ed)
+static void start_urb_unlink (struct ohci_hcd *ohci, struct ed *ed)
 {    
-       unsigned int            frame;
-       struct ohci_hcd         *ohci = hcd_to_ohci (usb_dev->bus->hcpriv);
-
        /* already pending? */
        if (ed->state & ED_URB_DEL)
                return;
@@ -503,9 +502,15 @@
                        break;
        }
 
-       frame = le16_to_cpu (ohci->hcca->frame_no) & 0x1;
-       ed->ed_rm_list = ohci->ed_rm_list [frame];
-       ohci->ed_rm_list [frame] = ed;
+       /* SF interrupt might get delayed; record the frame counter value that
+        * indicates when the HC isn't looking at it, so concurrent unlinks
+        * behave.  frame_no wraps every 2^16 msec, and changes right before
+        * SF is triggered.
+        */
+       ed->tick = le16_to_cpu (ohci->hcca->frame_no) + 1;
+
+       ed->ed_rm_list = ohci->ed_rm_list;
+       ohci->ed_rm_list = ed;
 
        /* enable SOF interrupt */
        if (!ohci->sleeping) {
@@ -605,7 +610,7 @@
        urb_priv->td_cnt = 0;
 
        if (data_len) {
-#ifdef CONFIG_PCI
+#if defined(CONFIG_PCI) || defined(CONFIG_SA1111)
                data = pci_map_single (ohci->hcd.pdev,
                        urb->transfer_buffer, data_len,
                        usb_pipeout (urb->pipe)
@@ -662,7 +667,7 @@
                        /* control requests don't use toggle state  */
                        info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
                        td_fill (ohci, info,
-#ifdef CONFIG_PCI
+#if defined(CONFIG_PCI) || defined(CONFIG_SA1111)
                                pci_map_single (ohci->hcd.pdev,
                                        urb->setup_packet, 8,
                                        PCI_DMA_TODEVICE),
@@ -816,10 +821,12 @@
                        if (td_list->ed->hwHeadP & ED_H) {
                                if (urb_priv && ((td_list->index + 1)
                                                < urb_priv->length)) {
+#ifdef OHCI_VERBOSE_DEBUG
                                        dbg ("urb %p TD %d of %d, patch ED",
                                                td_list->urb,
                                                1 + td_list->index,
                                                urb_priv->length);
+#endif
                                        td_list->ed->hwHeadP = 
                            (urb_priv->td [urb_priv->length - 1]->hwNextTD
                                    & __constant_cpu_to_le32 (TD_MASK))
@@ -841,27 +848,37 @@
 
 /*-------------------------------------------------------------------------*/
 
-/* there are some pending requests to unlink 
- * - some URBs/TDs if urb_priv->state == URB_DEL
- */
-static void dl_del_list (struct ohci_hcd *ohci, unsigned int frame)
+/* wrap-aware logic stolen from <linux/jiffies.h> */
+#define tick_before(t1,t2) ((((s16)(t1))-((s16)(t2))) < 0)
+
+/* there are some urbs/eds to unlink; called in_irq(), with HCD locked */
+static void finish_unlinks (struct ohci_hcd *ohci, u16 tick)
 {
-       unsigned long   flags;
-       struct ed       *ed;
-       __u32           edINFO;
-       __u32           tdINFO;
-       struct td       *td = NULL, *td_next = NULL,
-                       *tdHeadP = NULL, *tdTailP;
-       __u32           *td_p;
+       struct ed       *ed, **last;
        int             ctrl = 0, bulk = 0;
 
-       spin_lock_irqsave (&ohci->lock, flags);
-
-       for (ed = ohci->ed_rm_list [frame]; ed != NULL; ed = ed->ed_rm_list) {
+       for (last = &ohci->ed_rm_list, ed = *last; ed != NULL; ed = *last) {
+               struct td       *td, *td_next, *tdHeadP, *tdTailP;
+               u32             *td_p;
+               int             unlinked;
+
+               /* only take off EDs that the HC isn't using, accounting for
+                * frame counter wraps.  completion callbacks might prepend
+                * EDs to the list, they'll be checked next irq.
+                */
+               if (tick_before (tick, ed->tick)) {
+                       last = &ed->ed_rm_list;
+                       continue;
+               }
+               *last = ed->ed_rm_list;
+               ed->ed_rm_list = 0;
+               unlinked = 0;
 
+               /* unlink urbs from first one requested to queue end;
+                * leave earlier urbs alone
+                */
                tdTailP = dma_to_td (ohci, le32_to_cpup (&ed->hwTailP));
                tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP));
-               edINFO = le32_to_cpup (&ed->hwINFO);
                td_p = &ed->hwHeadP;
 
                for (td = tdHeadP; td != tdTailP; td = td_next) {
@@ -870,8 +887,11 @@
 
                        td_next = dma_to_td (ohci,
                                le32_to_cpup (&td->hwNextTD));
-                       if ((urb_priv->state == URB_DEL)) {
-                               tdINFO = le32_to_cpup (&td->hwINFO);
+                       if (unlinked || (urb_priv->state == URB_DEL)) {
+                               u32 tdINFO = le32_to_cpup (&td->hwINFO);
+
+                               unlinked = 1;
+
                                /* HC may have partly processed this TD */
                                if (TD_CC_GET (tdINFO) < 0xE)
                                        td_done (urb, td);
@@ -880,22 +900,32 @@
 
                                /* URB is done; clean up */
                                if (++ (urb_priv->td_cnt) == urb_priv->length) {
-                                       spin_unlock_irqrestore (&ohci->lock,
-                                               flags);
+                                       if (urb->status == -EINPROGRESS)
+                                               urb->status = -ECONNRESET;
+                                       spin_unlock (&ohci->lock);
                                        finish_urb (ohci, urb);
-                                       spin_lock_irqsave (&ohci->lock, flags);
+                                       spin_lock (&ohci->lock);
                                }
                        } else {
                                td_p = &td->hwNextTD;
                        }
                }
 
+               /* FIXME actually want four cases here:
+                * (a) finishing URB unlink
+                *     [a1] no URBs queued, so start ED unlink
+                *     [a2] some (earlier) URBs still linked, re-enable
+                * (b) finishing ED unlink
+                *     [b1] no URBs queued, ED is truly idle now
+                *     [b2] URBs now queued, link ED back into schedule
+                * right now we only have (a)
+                */
                ed->state &= ~ED_URB_DEL;
                tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP));
 
                if (tdHeadP == tdTailP) {
                        if (ed->state == ED_OPER)
-                               ep_unlink (ohci, ed);
+                               start_ed_unlink (ohci, ed);
                        td_free (ohci, tdTailP);
                        ed->hwINFO = ED_SKIP;
                        ed->state = ED_NEW;
@@ -918,7 +948,7 @@
                        writel (0, &ohci->regs->ed_controlcurrent);
                if (bulk)       /* reset bulk list */
                        writel (0, &ohci->regs->ed_bulkcurrent);
-               if (!ohci->ed_rm_list [!frame]) {
+               if (!ohci->ed_rm_list) {
                        if (ohci->ed_controltail)
                                ohci->hc_control |= OHCI_CTRL_CLE;
                        if (ohci->ed_bulktail)
@@ -926,9 +956,6 @@
                        writel (ohci->hc_control, &ohci->regs->control);   
                }
        }
-
-       ohci->ed_rm_list [frame] = NULL;
-       spin_unlock_irqrestore (&ohci->lock, flags);
 }
 
 
@@ -939,7 +966,7 @@
  * Process normal completions (error or success) and clean the schedules.
  *
  * This is the main path for handing urbs back to drivers.  The only other
- * path is dl_del_list(), which unlinks URBs by scanning EDs, instead of
+ * path is finish_unlinks(), which unlinks URBs using ed_rm_list, instead of
  * scanning the (re-reversed) donelist as this does.
  */
 static void dl_done_list (struct ohci_hcd *ohci, struct td *td)
@@ -960,7 +987,7 @@
                /* If all this urb's TDs are done, call complete().
                 * Interrupt transfers are the only special case:
                 * they're reissued, until "deleted" by usb_unlink_urb
-                * (real work done in a SOF intr, by dl_del_list).
+                * (real work done in a SOF intr, by finish_unlinks).
                 */
                if (urb_priv->td_cnt == urb_priv->length) {
                        int     resubmit;
@@ -980,7 +1007,7 @@
                if ((ed->hwHeadP & __constant_cpu_to_le32 (TD_MASK))
                                        == ed->hwTailP
                                && (ed->state == ED_OPER)) 
-                       ep_unlink (ohci, ed);
+                       start_ed_unlink (ohci, ed);
                td = td_next;
        }  
        spin_unlock_irqrestore (&ohci->lock, flags);
diff -X dontdiff.txt -Naur linux-2.5.20/drivers/usb/host/ohci-sa1111.c 
linux-2.5.20-usbwork/drivers/usb/host/ohci-sa1111.c
--- linux-2.5.20/drivers/usb/host/ohci-sa1111.c Wed Dec 31 16:00:00 1969
+++ linux-2.5.20-usbwork/drivers/usb/host/ohci-sa1111.c Wed Jun  5 18:37:58 2002
@@ -0,0 +1,315 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <[EMAIL PROTECTED]>
+ * (C) Copyright 2000-2002 David Brownell <[EMAIL PROTECTED]>
+ * 
+ * [ Initialisation is based on Linus'  ]
+ * [ uhci code and gregs ohci fragments ]
+ * [ (C) Copyright 1999 Linus Torvalds  ]
+ * [ (C) Copyright 1999 Gregory P. Smith]
+ * 
+ * SA1111 Bus Glue
+ *
+ * Written by Christopher Hoover <[EMAIL PROTECTED]>
+ * Based on fragments of previous driver by Rusell King et al.
+ *
+ * This file is licenced under the GPL.
+ */
+ 
+#include <asm/hardware.h>
+#include <asm/arch/assabet.h>
+#include <asm/arch/badge4.h>
+#include <asm/hardware/sa1111.h>
+
+/*-------------------------------------------------------------------------*/
+
+static void sa1111_start_hc(void)
+{
+       unsigned int usb_rst = 0;
+
+       printk(KERN_DEBUG __FILE__ 
+              ": starting SA-1111 OHCI USB Controller\n");
+
+#ifdef CONFIG_SA1100_BADGE4
+       if (machine_is_badge4()) {
+               badge4_set_5V(BADGE4_5V_USB, 1);
+       }
+#endif
+
+       if (machine_is_xp860() ||
+           machine_has_neponset() ||
+           machine_is_pfs168() ||
+           machine_is_badge4())
+               usb_rst = USB_RESET_PWRSENSELOW | USB_RESET_PWRCTRLLOW;
+
+       /*
+        * Configure the power sense and control lines.  Place the USB
+        * host controller in reset.
+        */
+       USB_RESET = usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET;
+
+       /*
+        * Now, carefully enable the USB clock, and take
+        * the USB host controller out of reset.
+        */
+       SKPCR |= SKPCR_UCLKEN;
+       udelay(11);
+       USB_RESET = usb_rst;
+}
+
+static void sa1111_stop_hc(void)
+{
+       printk(KERN_DEBUG __FILE__ 
+              ": stopping SA-1111 OHCI USB Controller\n");
+
+       /*
+        * Put the USB host controller into reset.
+        */
+       USB_RESET |= USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET;
+
+       /*
+        * Stop the USB clock.
+        */
+       SKPCR &= ~SKPCR_UCLKEN;
+
+#ifdef CONFIG_SA1100_BADGE4
+       if (machine_is_badge4()) {
+               /* Disable power to the USB bus */
+               badge4_set_5V(BADGE4_5V_USB, 0);
+       }
+#endif
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void dump_hci_status(const char *label)
+{
+       unsigned long status = USB_STATUS; 
+
+       dbg ("%s USB_STATUS = { %s%s%s%s%s}", label,
+            ((status & USB_STATUS_IRQHCIRMTWKUP) ? "IRQHCIRMTWKUP " : ""),
+            ((status & USB_STATUS_IRQHCIBUFFACC) ? "IRQHCIBUFFACC " : ""),
+            ((status & USB_STATUS_NIRQHCIM) ? "" : "IRQHCIM "),
+            ((status & USB_STATUS_NHCIMFCLR) ? "" : "HCIMFCLR "),
+            ((status & USB_STATUS_USBPWRSENSE) ? "USBPWRSENSE " : ""));
+}
+
+static void usb_hcd_sa1111_hcim_irq (int irq, void *__hcd, struct pt_regs * r)
+{
+       //dump_hci_status("irq");
+
+#if 0
+       /* may work better this way -- need to investigate further */
+       if (USB_STATUS & USB_STATUS_NIRQHCIM) {
+               //dbg ("not normal HC interrupt; ignoring");
+               return;
+       }
+#endif
+
+       usb_hcd_irq(irq, __hcd, r);
+}
+
+/*-------------------------------------------------------------------------*/
+
+void usb_hcd_sa1111_remove (struct usb_hcd *);
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+
+/**
+ * usb_hcd_sa1111_probe - initialize SA-1111-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ * Store this function in the HCD's struct pci_driver as probe().
+ */
+int usb_hcd_sa1111_probe (const struct hc_driver *driver, struct usb_hcd **hcd_out)
+{
+       int retval;
+       struct usb_hcd *hcd = 0;
+
+       if (!sa1111)
+               return -ENODEV;
+
+       if (!request_mem_region(_USB_OHCI_OP_BASE, 
+                               _USB_EXTENT, hcd_name)) {
+               dbg("request_mem_region failed");
+               return -EBUSY;
+       }
+
+       sa1111_start_hc();
+
+       hcd = driver->hcd_alloc ();
+       if (hcd == NULL){
+               dbg ("hcd_alloc failed");
+               retval = -ENOMEM;
+               goto err1;
+       }
+
+       hcd->driver = (struct hc_driver *) driver;
+       hcd->description = driver->description;
+       hcd->irq = NIRQHCIM;
+       hcd->regs = (void *) &USB_OHCI_OP_BASE;
+       hcd->pdev = SA1111_FAKE_PCIDEV;
+
+       retval = request_irq (hcd->irq, usb_hcd_sa1111_hcim_irq, SA_SHIRQ,
+                             hcd->description, hcd);
+       if (retval != 0) {
+               dbg("request_irq failed");
+               retval = -EBUSY;
+               goto err2;
+       }
+       set_irq_type(NIRQHCIM, IRQT_RISING);
+
+       info ("%s (SA-1111) at 0x%p, irq %d\n",
+             hcd->description, hcd->regs, hcd->irq);
+
+       usb_init_bus (&hcd->self);
+       hcd->self.op = &hcd_operations;
+       hcd->self.hcpriv = (void *) hcd;
+       hcd->self.bus_name = "SA-1111";
+       hcd->product_desc = "SA-1111 OHCI";
+
+       INIT_LIST_HEAD (&hcd->dev_list);
+
+       usb_register_bus (&hcd->self);
+
+       if ((retval = driver->start (hcd)) < 0) 
+       {
+               usb_hcd_sa1111_remove(hcd);
+               return retval;
+       }
+
+       *hcd_out = hcd;
+       return 0;
+
+ err2:
+       if (hcd) driver->hcd_free(hcd);
+ err1:
+       sa1111_stop_hc();
+       release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT);
+       return retval;
+}
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_sa1111_remove - shutdown processing for SA-1111-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_sa1111_probe(), first invoking
+ * the HCD's stop() method.  It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+void usb_hcd_sa1111_remove (struct usb_hcd *hcd)
+{
+       struct usb_device       *hub;
+       void *base;
+
+       info ("remove: %s, state %x", hcd->self.bus_name, hcd->state);
+
+       if (in_interrupt ()) BUG ();
+
+       hub = hcd->self.root_hub;
+       hcd->state = USB_STATE_QUIESCING;
+
+       dbg ("%s: roothub graceful disconnect", hcd->self.bus_name);
+       usb_disconnect (&hub);
+
+       hcd->driver->stop (hcd);
+       hcd->state = USB_STATE_HALT;
+
+       free_irq (hcd->irq, hcd);
+
+       usb_deregister_bus (&hcd->self);
+       if (atomic_read (&hcd->self.refcnt) != 1)
+               err (__FUNCTION__ ": %s, count != 1", hcd->self.bus_name);
+
+       base = hcd->regs;
+       hcd->driver->hcd_free (hcd);
+
+       sa1111_stop_hc();
+       release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ohci_sa1111_hc_driver = {
+       description:            hcd_name,
+
+       /*
+        * generic hardware linkage
+        */
+       irq:                    ohci_irq,
+       flags:                  HCD_USB11,
+
+       /*
+        * basic lifecycle operations
+        */
+       start:                  ohci_start,
+#ifdef CONFIG_PM
+       /* suspend:             ohci_suspend,  -- tbd */
+       /* resume:              ohci_resume,   -- tbd */
+#endif
+       stop:                   ohci_stop,
+
+       /*
+        * memory lifecycle (except per-request)
+        */
+       hcd_alloc:              ohci_hcd_alloc,
+       hcd_free:               ohci_hcd_free,
+
+       /*
+        * managing i/o requests and associated device resources
+        */
+       urb_enqueue:            ohci_urb_enqueue,
+       urb_dequeue:            ohci_urb_dequeue,
+       free_config:            ohci_free_config,
+
+       /*
+        * scheduling support
+        */
+       get_frame_number:       ohci_get_frame,
+
+       /*
+        * root hub support
+        */
+       hub_status_data:        ohci_hub_status_data,
+       hub_control:            ohci_hub_control,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* Only one SA-1111 ever exists. */
+static struct usb_hcd *the_sa1111_hcd;
+
+static int __init ohci_hcd_sa1111_init (void) 
+{
+       dbg (DRIVER_INFO " (SA-1111)");
+       dbg ("block sizes: ed %d td %d",
+               sizeof (struct ed), sizeof (struct td));
+
+       the_sa1111_hcd = 0;
+       return usb_hcd_sa1111_probe(&ohci_sa1111_hc_driver, &the_sa1111_hcd);
+}
+module_init (ohci_hcd_sa1111_init);
+
+
+static void __exit ohci_hcd_sa1111_cleanup (void) 
+{      
+       if (the_sa1111_hcd) {
+               usb_hcd_sa1111_remove(the_sa1111_hcd);
+               the_sa1111_hcd = 0;
+       }
+}
+module_exit (ohci_hcd_sa1111_cleanup);

_______________________________________________________________

Don't miss the 2002 Sprint PCS Application Developer's Conference
August 25-28 in Las Vegas -- http://devcon.sprintpcs.com/adp/index.cfm

_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to