[PATCH] SA-1111 support for ohci-hcd

This patch is a first cut at SA-1111 support for ohci-hcd.  It seems
to limp along OK.

I intentionally tried to change as little as possible for this first
cut.  Therefore the organization of the code could be improved -- I
don't care for the .c includes, but that style was already there.
More could be done with additional work, and perhaps the new driver
framework will be of some help.  Suggestions are welcome.

N.B. This patch includes David Brownell <[EMAIL PROTECTED]>'s patch
(ref: greg kh's usb-2.5 bk tree: [EMAIL PROTECTED], 2002-06-05
12:17:50-07:00, [EMAIL PROTECTED]).

Applies to 2.5.18-rmk1.  Needs to be backported to 2.4.x.


PATCH FOLLOWS
KernelVersion: 2.5.18-rmk1
diff -X ../dontdiff.txt -Naur linux-2.5.18-rmk1/drivers/usb/core/hcd.c 
linux-2.5.18-rmk1-ch1/drivers/usb/core/hcd.c
--- linux-2.5.18-rmk1/drivers/usb/core/hcd.c    Fri May 24 18:55:28 2002
+++ linux-2.5.18-rmk1-ch1/drivers/usb/core/hcd.c        Wed Jun  5 02:37:03 2002
@@ -44,9 +44,9 @@
 
 
 #ifdef CONFIG_USB_DEBUG
-       #define DEBUG
+#      define DEBUG
 #else
-       #undef DEBUG
+#      undef DEBUG
 #endif
 
 #include <linux/usb.h>
@@ -58,7 +58,6 @@
 #include <asm/unaligned.h>
 #include <asm/byteorder.h>
 
-
 // #define USB_BANDWIDTH_MESSAGES
 
 /*-------------------------------------------------------------------------*/
@@ -112,7 +111,8 @@
 /* used when updating hcd data */
 static spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED;
 
-static struct usb_operations hcd_operations;
+struct usb_operations hcd_operations;
+EXPORT_SYMBOL(hcd_operations);
 
 /*-------------------------------------------------------------------------*/
 
@@ -590,7 +590,7 @@
 /*-------------------------------------------------------------------------*/
 
 /* shared initialization code */
-static void usb_init_bus (struct usb_bus *bus)
+void usb_init_bus (struct usb_bus *bus)
 {
        memset (&bus->devmap, 0, sizeof(struct usb_devmap));
 
@@ -609,6 +609,7 @@
 
        atomic_set (&bus->refcnt, 1);
 }
+EXPORT_SYMBOL (usb_init_bus);
 
 /**
  * usb_alloc_bus - creates a new USB host controller structure
@@ -919,7 +920,7 @@
 /* PCI-based HCs are normal, but custom bus glue should be ok */
 
 static void hcd_irq (int irq, void *__hcd, struct pt_regs *r);
-static void hc_died (struct usb_hcd *hcd);
+static void usb_hc_died (struct usb_hcd *hcd);
 
 /*-------------------------------------------------------------------------*/
 
@@ -1215,7 +1216,7 @@
        retval = hcd->driver->resume (hcd);
        if (!HCD_IS_RUNNING (hcd->state)) {
                dbg ("resume %s failure, retval %d", hcd->self.bus_name, retval);
-               hc_died (hcd);
+               usb_hc_died (hcd);
 // FIXME:  recover, reset etc.
        } else {
                // FIXME for all connected devices, root-to-leaf:
@@ -1275,7 +1276,7 @@
 
 /*-------------------------------------------------------------------------*/
 
-static void hc_died (struct usb_hcd *hcd)
+void usb_hc_died (struct usb_hcd *hcd)
 {
        struct list_head        *devlist, *urblist;
        struct hcd_dev          *dev;
@@ -1303,6 +1304,7 @@
                rh_status_dequeue (hcd, urb);
        hcd->driver->stop (hcd);
 }
+EXPORT_SYMBOL(usb_hc_died);
 
 /*-------------------------------------------------------------------------*/
 
@@ -1741,7 +1743,7 @@
        return 0;
 }
 
-static struct usb_operations hcd_operations = {
+struct usb_operations hcd_operations = {
        allocate:               hcd_alloc_dev,
        get_frame_number:       hcd_get_frame_number,
        submit_urb:             hcd_submit_urb,
@@ -1751,7 +1753,7 @@
 
 /*-------------------------------------------------------------------------*/
 
-static void hcd_irq (int irq, void *__hcd, struct pt_regs * r)
+void usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r)
 {
        struct usb_hcd          *hcd = __hcd;
        int                     start = hcd->state;
@@ -1761,8 +1763,9 @@
 
        hcd->driver->irq (hcd);
        if (hcd->state != start && hcd->state == USB_STATE_HALT)
-               hc_died (hcd);
+               usb_hc_died (hcd);
 }
+EXPORT_SYMBOL (usb_hcd_irq);
 
 /*-------------------------------------------------------------------------*/
 
diff -X ../dontdiff.txt -Naur linux-2.5.18-rmk1/drivers/usb/core/hcd.h 
linux-2.5.18-rmk1-ch1/drivers/usb/core/hcd.h
--- linux-2.5.18-rmk1/drivers/usb/core/hcd.h    Fri May 24 18:55:23 2002
+++ linux-2.5.18-rmk1-ch1/drivers/usb/core/hcd.h        Wed Jun  5 02:25:26 2002
@@ -50,9 +50,9 @@
        int                     irq;            /* irq allocated */
        void                    *regs;          /* device memory/io */
 
-#ifdef CONFIG_PCI
        /* a few non-PCI controllers exist, mostly for OHCI */
        struct pci_dev          *pdev;          /* pci is typical */
+#ifdef CONFIG_PCI
        int                     region;         /* pci region for regs */
        u32                     pci_state [16]; /* for PM state save */
        atomic_t                resume_count;   /* multiple resumes issue */
@@ -161,6 +161,9 @@
 };
 
 extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb);
+extern void usb_hcd_irq (int irq, void *__hcd, struct pt_regs *r);
+extern void usb_hc_died (struct usb_hcd *hcd);
+
 
 #ifdef CONFIG_PCI
 struct pci_dev;
@@ -181,6 +184,8 @@
 
 /* -------------------------------------------------------------------------- */
 
+extern struct usb_operations hcd_operations;
+
 /* Enumeration is only for the hub driver, or HCD virtual root hubs */
 extern int usb_new_device(struct usb_device *dev);
 extern void usb_connect(struct usb_device *dev);
@@ -265,6 +270,7 @@
 
 /*-------------------------------------------------------------------------*/
 
+extern void usb_init_bus (struct usb_bus *bus);
 extern struct usb_bus *usb_alloc_bus (struct usb_operations *);
 extern void usb_free_bus (struct usb_bus *);
 
diff -X ../dontdiff.txt -Naur linux-2.5.18-rmk1/drivers/usb/host/ohci-hcd.c 
linux-2.5.18-rmk1-ch1/drivers/usb/host/ohci-hcd.c
--- linux-2.5.18-rmk1/drivers/usb/host/ohci-hcd.c       Fri May 24 18:55:19 2002
+++ linux-2.5.18-rmk1-ch1/drivers/usb/host/ohci-hcd.c   Wed Jun  5 02:20:01 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->ohci_dev->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.18-rmk1/drivers/usb/host/ohci.h 
linux-2.5.18-rmk1-ch1/drivers/usb/host/ohci.h
--- linux-2.5.18-rmk1/drivers/usb/host/ohci.h   Fri May 24 18:55:31 2002
+++ linux-2.5.18-rmk1-ch1/drivers/usb/host/ohci.h       Wed Jun  5 02:28:08 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.18-rmk1/drivers/usb/host/ohci-hub.c 
linux-2.5.18-rmk1-ch1/drivers/usb/host/ohci-hub.c
--- linux-2.5.18-rmk1/drivers/usb/host/ohci-hub.c       Fri May 24 18:55:20 2002
+++ linux-2.5.18-rmk1-ch1/drivers/usb/host/ohci-hub.c   Wed May 29 22:54:53 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.18-rmk1/drivers/usb/host/ohci-mem.c 
linux-2.5.18-rmk1-ch1/drivers/usb/host/ohci-mem.c
--- linux-2.5.18-rmk1/drivers/usb/host/ohci-mem.c       Fri May 24 18:55:28 2002
+++ linux-2.5.18-rmk1-ch1/drivers/usb/host/ohci-mem.c   Thu May 30 12:33:08 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.18-rmk1/drivers/usb/host/ohci-pci.c 
linux-2.5.18-rmk1-ch1/drivers/usb/host/ohci-pci.c
--- linux-2.5.18-rmk1/drivers/usb/host/ohci-pci.c       Wed Dec 31 16:00:00 1969
+++ linux-2.5.18-rmk1-ch1/drivers/usb/host/ohci-pci.c   Thu May 30 01:32:15 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 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_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 const struct hc_driver ohci_pci_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 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_pci_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.18-rmk1/drivers/usb/host/ohci-q.c 
linux-2.5.18-rmk1-ch1/drivers/usb/host/ohci-q.c
--- linux-2.5.18-rmk1/drivers/usb/host/ohci-q.c Fri May 24 18:55:30 2002
+++ linux-2.5.18-rmk1-ch1/drivers/usb/host/ohci-q.c     Sun Jun  2 15:43:29 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.18-rmk1/drivers/usb/host/ohci-sa1111.c 
linux-2.5.18-rmk1-ch1/drivers/usb/host/ohci-sa1111.c
--- linux-2.5.18-rmk1/drivers/usb/host/ohci-sa1111.c    Wed Dec 31 16:00:00 1969
+++ linux-2.5.18-rmk1-ch1/drivers/usb/host/ohci-sa1111.c        Wed Jun  5 02:12:32 
+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_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_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