]> git.neil.brown.name Git - history.git/commitdiff
USB SL811HS host controller driver.
authorGreg Kroah-Hartman <greg@kroah.com>
Tue, 28 May 2002 16:39:52 +0000 (09:39 -0700)
committerGreg Kroah-Hartman <greg@kroah.com>
Tue, 28 May 2002 16:39:52 +0000 (09:39 -0700)
Added the driver to the 2.5 tree.  The original code for 2.4 was
written by Pei Liu <pbl@cypress.com> but cleaned up a bit and
ported to 2.5 by me.  Any problems in the driver is probably due to
my messing with it.

This driver is for the SL811HS USB host controller chip from
Cypress and is typically contained on a ARM based embedded
system.

drivers/usb/Makefile
drivers/usb/host/Config.help
drivers/usb/host/Config.in
drivers/usb/host/Makefile
drivers/usb/host/hc_simple.c [new file with mode: 0644]
drivers/usb/host/hc_simple.h [new file with mode: 0644]
drivers/usb/host/hc_sl811.c [new file with mode: 0644]
drivers/usb/host/hc_sl811.h [new file with mode: 0644]
drivers/usb/host/hc_sl811_rh.c [new file with mode: 0644]

index c4549b871006458e2c4b59dc60b4a4dade465a1f..c1695ebe8f18f51bb27ed4b7d715f3f75ec18423 100644 (file)
@@ -12,6 +12,7 @@ subdir-$(CONFIG_USB)          += core
 subdir-$(CONFIG_USB_EHCI_HCD)          += host
 subdir-$(CONFIG_USB_OHCI_HCD)          += host
 subdir-$(CONFIG_USB_OHCI)              += host
+subdir-$(CONFIG_USB_SL811HS)           += host
 subdir-$(CONFIG_USB_UHCI_ALT)          += host
 subdir-$(CONFIG_USB_UHCI_HCD_ALT)      += host
 subdir-$(CONFIG_USB_UHCI_HCD)          += host
index 58ffefb7862c9b2e7ee9134dd1ac71260806219a..2456b823e2690805fe12d274ef7e9836022ebf9c 100644 (file)
@@ -89,3 +89,13 @@ CONFIG_USB_OHCI
   inserted in and removed from the running kernel whenever you want).
   The module will be called usb-ohci.o. If you want to compile it
   as a module, say M here and read <file:Documentation/modules.txt>.
+
+CONFIG_USB_SL811HS
+  Say Y here if you have a SL811HS USB host controller in your system.
+
+  If you do not know what this is, please say N.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called hc_sl811.o. If you want to compile it
+  as a module, say M here and read <file:Documentation/modules.txt>.
index edf103166ac2a347fc9321fbc4059dad0fce13c9..2afce8e137d8cd923d852c3b5817c476ad4d4536 100644 (file)
@@ -19,3 +19,6 @@ fi
 #   define_bool CONFIG_USB_UHCI_ALT n
 #fi
 #dep_tristate '  OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB
+if [ "$CONFIG_ARM" = "y" ]; then
+   dep_tristate '  SL811HS support' CONFIG_USB_SL811HS $CONFIG_USB
+fi
index 14bb932676fd375d5fa586e4561de0940848ae89..b314d710871198a0cb18d50ac2f9cbae90684818 100644 (file)
@@ -13,5 +13,6 @@ obj-$(CONFIG_USB_UHCI_HCD_ALT)        += uhci-hcd.o
 obj-$(CONFIG_USB_UHCI)         += usb-uhci.o
 obj-$(CONFIG_USB_UHCI_ALT)     += uhci.o
 obj-$(CONFIG_USB_OHCI)         += usb-ohci.o
+obj-$(CONFIG_USB_SL811HS)      += hc_sl811.o
 
 include $(TOPDIR)/Rules.make
diff --git a/drivers/usb/host/hc_simple.c b/drivers/usb/host/hc_simple.c
new file mode 100644 (file)
index 0000000..1170cbd
--- /dev/null
@@ -0,0 +1,1074 @@
+/*-------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------*
+ * simple generic USB HCD frontend Version 0.9.5 (10/28/2001)
+ * for embedded HCs (SL811HS)
+ * 
+ * USB URB handling, hci_ hcs_
+ * URB queueing, qu_
+ * Transfer scheduling, sh_
+ * 
+ *
+ *-------------------------------------------------------------------------*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *-------------------------------------------------------------------------*/
+
+/* main lock for urb access */
+static spinlock_t usb_urb_lock = SPIN_LOCK_UNLOCKED;
+
+/*-------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------*/
+/* URB HCD API function layer
+ * * * */
+
+/***************************************************************************
+ * Function Name : hcs_urb_queue
+ *
+ * This function initializes the urb status and length before queueing the 
+ * urb. 
+ *
+ * Input:  hci = data structure for the host controller
+ *         urb = USB request block data structure 
+ *
+ * Return: 0 
+ **************************************************************************/
+static inline int hcs_urb_queue (hci_t * hci, struct urb * urb)
+{
+       int i;
+
+       DBGFUNC ("enter hcs_urb_queue\n");
+       if (usb_pipeisoc (urb->pipe)) {
+               DBGVERBOSE ("hcs_urb_queue: isoc pipe\n");
+               for (i = 0; i < urb->number_of_packets; i++) {
+                       urb->iso_frame_desc[i].actual_length = 0;
+                       urb->iso_frame_desc[i].status = -EXDEV;
+               }
+
+               /* urb->next hack : 1 .. resub, 0 .. single shot */
+               /* urb->interval = urb->next ? 1 : 0; */
+       }
+
+       urb->status = -EINPROGRESS;
+       urb->actual_length = 0;
+       urb->error_count = 0;
+
+       if (usb_pipecontrol (urb->pipe))
+               hc_flush_data_cache (hci, urb->setup_packet, 8);
+       if (usb_pipeout (urb->pipe))
+               hc_flush_data_cache (hci, urb->transfer_buffer,
+                                    urb->transfer_buffer_length);
+
+       qu_queue_urb (hci, urb);
+
+       return 0;
+}
+
+/***************************************************************************
+ * Function Name : hcs_return_urb
+ *
+ * This function the return path of URB back to the USB core. It calls the
+ * the urb complete function if exist, and also handles the resubmition of
+ * interrupt URBs.
+ *
+ * Input:  hci = data structure for the host controller
+ *         urb = USB request block data structure 
+ *         resub_ok = resubmit flag: 1 = submit urb again, 0 = not submit 
+ *
+ * Return: 0 
+ **************************************************************************/
+static int hcs_return_urb (hci_t * hci, struct urb * urb, int resub_ok)
+{
+       struct usb_device *dev = urb->dev;
+       int resubmit = 0;
+
+       DBGFUNC ("enter hcs_return_urb, urb pointer = 0x%x, "
+                "transferbuffer point = 0x%x, "
+                " setup packet pointer = 0x%x, context pointer = 0x%x \n",
+                (__u32 *) urb, (__u32 *) urb->transfer_buffer,
+                (__u32 *) urb->setup_packet, (__u32 *) urb->context);
+       if (urb_debug)
+               urb_print (urb, "RET", usb_pipeout (urb->pipe));
+
+       resubmit = urb->interval && resub_ok;
+
+       urb->dev = urb->hcpriv = NULL;
+
+       if (urb->complete) {
+               urb->complete (urb);    /* call complete */
+       }
+
+       if (resubmit) {
+               /* requeue the URB */
+               urb->dev = dev;
+               hcs_urb_queue (hci, urb);
+       } else {
+               usb_put_urb(urb);
+       }
+
+       return 0;
+}
+
+/***************************************************************************
+ * Function Name : hci_submit_urb
+ *
+ * This function is called by the USB core API when an URB is available to
+ * process.  This function does the following
+ *
+ * 1) Check the validity of the URB
+ * 2) Parse the device number from the URB
+ * 3) Pass the URB to the root hub routine if its intended for the hub, else
+ *    queue the urb for the attached device. 
+ *
+ * Input: urb = USB request block data structure 
+ *
+ * Return: 0 if success or error code 
+ **************************************************************************/
+static int hci_submit_urb (struct urb * urb, int mem_flags)
+{
+       hci_t *hci;
+       unsigned int pipe = urb->pipe;
+       unsigned long flags;
+       int ret;
+
+       DBGFUNC ("enter hci_submit_urb, pipe = 0x%x\n", urb->pipe);
+       if (!urb->dev || !urb->dev->bus || urb->hcpriv)
+               return -EINVAL;
+
+       if (usb_endpoint_halted
+           (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) {
+               printk ("hci_submit_urb: endpoint_halted\n");
+               return -EPIPE;
+       }
+       hci = (hci_t *) urb->dev->bus->hcpriv;
+
+       /* a request to the virtual root hub */
+       if (usb_pipedevice (pipe) == hci->rh.devnum) {
+               if (urb_debug > 1)
+                       urb_print (urb, "SUB-RH", usb_pipein (pipe));
+
+               return rh_submit_urb (urb);
+       }
+
+       /* increment urb's reference count, we now control it. */
+       urb = usb_get_urb (urb);
+
+       /* queue the URB to its endpoint-queue */
+       spin_lock_irqsave (&usb_urb_lock, flags);
+       ret = hcs_urb_queue (hci, urb);
+       if (ret != 0) {
+               /* error on return */
+               DBGERR ("hci_submit_urb: return err, ret = 0x%x, urb->status = 0x%x\n",
+                       ret, urb->status);
+               usb_put_urb (urb);
+       }
+
+       spin_unlock_irqrestore (&usb_urb_lock, flags);
+
+       return ret;
+
+}
+
+/***************************************************************************
+ * Function Name : hci_unlink_urb
+ *
+ * This function mark the URB to unlink
+ *
+ * Input: urb = USB request block data structure 
+ *
+ * Return: 0 if success or error code 
+ **************************************************************************/
+static int hci_unlink_urb (struct urb * urb)
+{
+       unsigned long flags;
+       hci_t *hci;
+       DECLARE_WAITQUEUE (wait, current);
+       void *comp = NULL;
+
+       DBGFUNC ("enter hci_unlink_urb\n");
+
+       if (!urb)               /* just to be sure */
+               return -EINVAL;
+
+       if (!urb->dev || !urb->dev->bus)
+               return -ENODEV;
+
+       hci = (hci_t *) urb->dev->bus->hcpriv;
+
+       /* a request to the virtual root hub */
+       if (usb_pipedevice (urb->pipe) == hci->rh.devnum) {
+               return rh_unlink_urb (urb);
+       }
+
+       if (urb_debug)
+               urb_print (urb, "UNLINK", 1);
+
+       spin_lock_irqsave (&usb_urb_lock, flags);
+
+       if (!list_empty (&urb->urb_list) && urb->status == -EINPROGRESS) {
+               /* URB active? */
+
+               if (urb->transfer_flags & (USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED)) {
+                       /* asynchron with callback */
+
+                       list_del (&urb->urb_list);      /* relink the urb to the del list */
+                       list_add (&urb->urb_list, &hci->del_list);
+                       spin_unlock_irqrestore (&usb_urb_lock, flags);
+
+               } else {
+                       /* synchron without callback */
+
+                       add_wait_queue (&hci->waitq, &wait);
+
+                       set_current_state (TASK_UNINTERRUPTIBLE);
+                       comp = urb->complete;
+                       urb->complete = NULL;
+
+                       list_del (&urb->urb_list);      /* relink the urb to the del list */
+                       list_add (&urb->urb_list, &hci->del_list);
+
+                       spin_unlock_irqrestore (&usb_urb_lock, flags);
+
+                       schedule_timeout (HZ / 50);
+
+                       if (!list_empty (&urb->urb_list))
+                               list_del (&urb->urb_list);
+
+                       urb->complete = comp;
+                       urb->hcpriv = NULL;
+                       remove_wait_queue (&hci->waitq, &wait);
+               }
+       } else {
+               /* hcd does not own URB but we keep the driver happy anyway */
+               spin_unlock_irqrestore (&usb_urb_lock, flags);
+
+               if (urb->complete && (urb->transfer_flags & USB_ASYNC_UNLINK)) {
+                       urb->status = -ENOENT;
+                       urb->actual_length = 0;
+                       urb->complete (urb);
+                       urb->status = 0;
+               } else {
+                       urb->status = -ENOENT;
+               }
+       }
+
+       return 0;
+}
+
+/***************************************************************************
+ * Function Name : hci_alloc_dev
+ *
+ * This function allocates private data space for the usb device and 
+ * initialize the endpoint descriptor heads.
+ *
+ * Input: usb_dev = pointer to the usb device 
+ *
+ * Return: 0 if success or error code 
+ **************************************************************************/
+static int hci_alloc_dev (struct usb_device *usb_dev)
+{
+       struct hci_device *dev;
+       int i;
+
+       DBGFUNC ("enter hci_alloc_dev\n");
+       dev = kmalloc (sizeof (*dev), GFP_KERNEL);
+       if (!dev)
+               return -ENOMEM;
+
+       memset (dev, 0, sizeof (*dev));
+
+       for (i = 0; i < 32; i++) {
+               INIT_LIST_HEAD (&(dev->ed[i].urb_queue));
+               dev->ed[i].pipe_head = NULL;
+       }
+
+       usb_dev->hcpriv = dev;
+
+       DBGVERBOSE ("USB HC dev alloc %d bytes\n", sizeof (*dev));
+
+       return 0;
+
+}
+
+/***************************************************************************
+ * Function Name : hci_free_dev
+ *
+ * This function de-allocates private data space for the usb devic
+ *
+ * Input: usb_dev = pointer to the usb device 
+ *
+ * Return: 0  
+ **************************************************************************/
+static int hci_free_dev (struct usb_device *usb_dev)
+{
+       DBGFUNC ("enter hci_free_dev\n");
+
+       if (usb_dev->hcpriv)
+               kfree (usb_dev->hcpriv);
+
+       usb_dev->hcpriv = NULL;
+
+       return 0;
+}
+
+/***************************************************************************
+ * Function Name : hci_get_current_frame_number
+ *
+ * This function get the current USB frame number
+ *
+ * Input: usb_dev = pointer to the usb device 
+ *
+ * Return: frame number  
+ **************************************************************************/
+static int hci_get_current_frame_number (struct usb_device *usb_dev)
+{
+       hci_t *hci = usb_dev->bus->hcpriv;
+       DBGFUNC ("enter hci_get_current_frame_number, frame = 0x%x \r\n",
+                hci->frame_number);
+
+       return (hci->frame_number);
+}
+
+/***************************************************************************
+ * List of all io-functions 
+ **************************************************************************/
+
+static struct usb_operations hci_device_operations = {
+       allocate:               hci_alloc_dev,
+       deallocate:             hci_free_dev,
+       get_frame_number:       hci_get_current_frame_number,
+       submit_urb:             hci_submit_urb,
+       unlink_urb:             hci_unlink_urb,
+};
+
+/***************************************************************************
+ * URB queueing:
+ * 
+ * For each type of transfer (INTR, BULK, ISO, CTRL) there is a list of 
+ * active URBs.
+ * (hci->intr_list, hci->bulk_list, hci->iso_list, hci->ctrl_list)
+ * For every endpoint the head URB of the queued URBs is linked to one of 
+ * those lists.
+ * 
+ * The rest of the queued URBs of an endpoint are linked into a 
+ * private URB list for each endpoint. (hci_dev->ed [endpoint_io].urb_queue)
+ * hci_dev->ed [endpoint_io].pipe_head .. points to the head URB which is 
+ * in one of the active URB lists.
+ * 
+ * The index of an endpoint consists of its number and its direction.
+ * 
+ * The state of an intr and iso URB is 0. 
+ * For ctrl URBs the states are US_CTRL_SETUP, US_CTRL_DATA, US_CTRL_ACK
+ * Bulk URBs states are US_BULK and US_BULK0 (with 0-len packet)
+ * 
+ **************************************************************************/
+
+/***************************************************************************
+ * Function Name : qu_urb_timeout
+ *
+ * This function is called when the URB timeout. The function unlinks the 
+ * URB. 
+ *
+ * Input: lurb: URB 
+ *
+ * Return: none  
+ **************************************************************************/
+#ifdef HC_URB_TIMEOUT
+static void qu_urb_timeout (unsigned long lurb)
+{
+       struct urb *urb = (struct urb *) lurb;
+
+       DBGFUNC ("enter qu_urb_timeout\n");
+       urb->transfer_flags |= USB_TIMEOUT_KILLED;
+       hci_unlink_urb (urb);
+}
+#endif
+
+/***************************************************************************
+ * Function Name : qu_pipeindex
+ *
+ * This function gets the index of the pipe.   
+ *
+ * Input: pipe: the urb pipe 
+ *
+ * Return: index  
+ **************************************************************************/
+static inline int qu_pipeindex (__u32 pipe)
+{
+       DBGFUNC ("enter qu_pipeindex\n");
+       return (usb_pipeendpoint (pipe) << 1) | (usb_pipecontrol (pipe) ? 0 : usb_pipeout (pipe));
+}
+
+/***************************************************************************
+ * Function Name : qu_seturbstate
+ *
+ * This function set the state of the URB.  
+ * 
+ * control pipe: 3 states -- Setup, data, status
+ * interrupt and bulk pipe: 1 state -- data    
+ *
+ * Input: urb = USB request block data structure 
+ *        state = the urb state
+ *
+ * Return: none  
+ **************************************************************************/
+static inline void qu_seturbstate (struct urb * urb, int state)
+{
+       DBGFUNC ("enter qu_seturbstate\n");
+       urb->pipe &= ~0x1f;
+       urb->pipe |= state & 0x1f;
+}
+
+/***************************************************************************
+ * Function Name : qu_urbstate
+ *
+ * This function get the current state of the URB.  
+ * 
+ * Input: urb = USB request block data structure 
+ *
+ * Return: none  
+ **************************************************************************/
+static inline int qu_urbstate (struct urb * urb)
+{
+
+       DBGFUNC ("enter qu_urbstate\n");
+
+       return urb->pipe & 0x1f;
+}
+
+/***************************************************************************
+ * Function Name : qu_queue_active_urb
+ *
+ * This function adds the urb to the appropriate active urb list and set
+ * the urb state.
+ * 
+ * There are four active lists: isochoronous list, interrupt list, 
+ * control list, and bulk list.
+ * 
+ * Input: hci = data structure for the host controller 
+ *        urb = USB request block data structure 
+ *        ed = endpoint descriptor
+ *
+ * Return: none  
+ **************************************************************************/
+static inline void qu_queue_active_urb (hci_t * hci, struct urb * urb, epd_t * ed)
+{
+       int urb_state = 0;
+       DBGFUNC ("enter qu_queue_active_urb\n");
+       switch (usb_pipetype (urb->pipe)) {
+       case PIPE_CONTROL:
+               list_add (&urb->urb_list, &hci->ctrl_list);
+               urb_state = US_CTRL_SETUP;
+               break;
+
+       case PIPE_BULK:
+               list_add (&urb->urb_list, &hci->bulk_list);
+               if ((urb->transfer_flags & USB_ZERO_PACKET)
+                   && urb->transfer_buffer_length > 0
+                   &&
+                   ((urb->transfer_buffer_length %
+                     usb_maxpacket (urb->dev, urb->pipe,
+                                    usb_pipeout (urb->pipe))) == 0)) {
+                       urb_state = US_BULK0;
+               }
+               break;
+
+       case PIPE_INTERRUPT:
+               urb->start_frame = hci->frame_number;
+               list_add (&urb->urb_list, &hci->intr_list);
+               break;
+
+       case PIPE_ISOCHRONOUS:
+               list_add (&urb->urb_list, &hci->iso_list);
+               break;
+       }
+
+#ifdef HC_URB_TIMEOUT
+       if (urb->timeout) {
+               ed->timeout.data = (unsigned long) urb;
+               ed->timeout.expires = urb->timeout + jiffies;
+               ed->timeout.function = qu_urb_timeout;
+               add_timer (&ed->timeout);
+       }
+#endif
+
+       qu_seturbstate (urb, urb_state);
+}
+
+/***************************************************************************
+ * Function Name : qu_queue_urb
+ *
+ * This function adds the urb to the endpoint descriptor list 
+ * 
+ * Input: hci = data structure for the host controller 
+ *        urb = USB request block data structure 
+ *
+ * Return: none  
+ **************************************************************************/
+static int qu_queue_urb (hci_t * hci, struct urb * urb)
+{
+       struct hci_device *hci_dev = usb_to_hci (urb->dev);
+       epd_t *ed = &hci_dev->ed[qu_pipeindex (urb->pipe)];
+
+       DBGFUNC ("Enter qu_queue_urb\n");
+
+       /* for ISOC transfers calculate start frame index */
+
+       if (usb_pipeisoc (urb->pipe) && urb->transfer_flags & USB_ISO_ASAP) {
+               urb->start_frame = ((ed->pipe_head) ? (ed->last_iso + 1) : hci_get_current_frame_number (urb-> dev) + 1) & 0xffff;
+       }
+
+       if (ed->pipe_head) {
+               __list_add (&urb->urb_list, ed->urb_queue.prev,
+                           &(ed->urb_queue));
+       } else {
+               ed->pipe_head = urb;
+               qu_queue_active_urb (hci, urb, ed);
+               if (++hci->active_urbs == 1)
+                       hc_start_int (hci);
+       }
+
+       return 0;
+}
+
+/***************************************************************************
+ * Function Name : qu_next_urb
+ *
+ * This function removes the URB from the queue and add the next URB to 
+ * active list. 
+ * 
+ * Input: hci = data structure for the host controller 
+ *        urb = USB request block data structure 
+ *        resub_ok = resubmit flag
+ *
+ * Return: pointer to the next urb  
+ **************************************************************************/
+static struct urb *qu_next_urb (hci_t * hci, struct urb * urb, int resub_ok)
+{
+       struct hci_device *hci_dev = usb_to_hci (urb->dev);
+       epd_t *ed = &hci_dev->ed[qu_pipeindex (urb->pipe)];
+
+       DBGFUNC ("enter qu_next_urb\n");
+       list_del (&urb->urb_list);
+       INIT_LIST_HEAD (&urb->urb_list);
+       if (ed->pipe_head == urb) {
+
+#ifdef HC_URB_TIMEOUT
+               if (urb->timeout)
+                       del_timer (&ed->timeout);
+#endif
+
+               if (!--hci->active_urbs)
+                       hc_stop_int (hci);
+
+               if (!list_empty (&ed->urb_queue)) {
+                       urb = list_entry (ed->urb_queue.next, struct urb, urb_list);
+                       list_del (&urb->urb_list);
+                       INIT_LIST_HEAD (&urb->urb_list);
+                       ed->pipe_head = urb;
+                       qu_queue_active_urb (hci, urb, ed);
+               } else {
+                       ed->pipe_head = NULL;
+                       urb = NULL;
+               }
+       }
+       return urb;
+}
+
+/***************************************************************************
+ * Function Name : qu_return_urb
+ *
+ * This function is part of the return path.   
+ * 
+ * Input: hci = data structure for the host controller 
+ *        urb = USB request block data structure 
+ *        resub_ok = resubmit flag
+ *
+ * Return: pointer to the next urb  
+ **************************************************************************/
+static struct urb *qu_return_urb (hci_t * hci, struct urb * urb, int resub_ok)
+{
+       struct urb *next_urb;
+
+       DBGFUNC ("enter qu_return_rub\n");
+       next_urb = qu_next_urb (hci, urb, resub_ok);
+       hcs_return_urb (hci, urb, resub_ok);
+       return next_urb;
+}
+
+/***************************************************************************
+ * Function Name : sh_scan_iso_urb_list
+ *
+ * This function goes throught the isochronous urb list and schedule the 
+ * the transfer.   
+ *
+ * Note: This function has not tested yet
+ * 
+ * Input: hci = data structure for the host controller 
+ *        list_lh = pointer to the isochronous list 
+ *        frame_number = the frame number 
+ *
+ * Return: 0 = unsuccessful; 1 = successful  
+ **************************************************************************/
+static int sh_scan_iso_urb_list (hci_t * hci, struct list_head *list_lh,
+                                int frame_number)
+{
+       struct list_head *lh = list_lh->next;
+       struct urb *urb;
+
+       DBGFUNC ("enter sh_scan_iso_urb_list\n");
+       hci->td_array->len = 0;
+
+       while (lh != list_lh) {
+               urb = list_entry (lh, struct urb, urb_list);
+               lh = lh->next;
+               if (((frame_number - urb->start_frame) & 0x7ff) <
+                   urb->number_of_packets) {
+                       if (!sh_add_packet (hci, urb)) {
+                               return 0;
+                       } else {
+                               if (((frame_number -
+                                     urb->start_frame) & 0x7ff) > 0x400) {
+                                       if (qu_urbstate (urb) > 0)
+                                               urb = qu_return_urb (hci, urb, 1);
+                                       else
+                                               urb = qu_next_urb (hci, urb, 1);
+
+                                       if (lh == list_lh && urb)
+                                               lh = &urb->urb_list;
+                               }
+                       }
+               }
+       }
+       return 1;
+}
+
+/***************************************************************************
+ * Function Name : sh_scan_urb_list
+ *
+ * This function goes through the urb list and schedule the 
+ * the transaction.   
+ * 
+ * Input: hci = data structure for the host controller 
+ *        list_lh = pointer to the isochronous list 
+ *
+ * Return: 0 = unsuccessful; 1 = successful  
+ **************************************************************************/
+static int sh_scan_urb_list (hci_t * hci, struct list_head *list_lh)
+{
+       struct list_head *lh = NULL;
+       struct urb *urb;
+
+       if (list_lh == NULL) {
+               DBGERR ("sh_scan_urb_list: error, list_lh == NULL\n");
+       }
+
+       DBGFUNC ("enter sh_scan_urb_list: frame# \n");
+
+       list_for_each (lh, list_lh) {
+               urb = list_entry (lh, struct urb, urb_list);
+               if (urb == NULL)
+                       return 1;
+               if (!usb_pipeint (urb->pipe)
+                   || (((hci->frame_number - urb->start_frame)
+                        & 0x7ff) >= urb->interval)) {
+                       DBGVERBOSE ("sh_scan_urb_list !INT: %d fr_no: %d int: %d pint: %d\n",
+                                   urb->start_frame, hci->frame_number, urb->interval,
+                                   usb_pipeint (urb->pipe));
+                       if (!sh_add_packet (hci, urb)) {
+                               return 0;
+                       } else {
+                               DBGVERBOSE ("INT: start: %d fr_no: %d int: %d pint: %d\n",
+                                           urb->start_frame, hci->frame_number,
+                                           urb->interval, usb_pipeint (urb->pipe));
+                               urb->start_frame = hci->frame_number;
+                               return 0;
+
+                       }
+               }
+       }
+       return 1;
+}
+
+/***************************************************************************
+ * Function Name : sh_shedule_trans
+ *
+ * This function schedule the USB transaction.
+ * This function will process the endpoint in the following order: 
+ * interrupt, control, and bulk.    
+ * 
+ * Input: hci = data structure for the host controller 
+ *        isSOF = flag indicate if Start Of Frame has occurred 
+ *
+ * Return: 0   
+ **************************************************************************/
+static int sh_schedule_trans (hci_t * hci, int isSOF)
+{
+       int units_left = 1;
+       struct list_head *lh;
+
+       if (hci == NULL) {
+               DBGERR ("sh_schedule_trans: hci == NULL\n");
+               return 0;
+       }
+       if (hci->td_array == NULL) {
+               DBGERR ("sh_schedule_trans: hci->td_array == NULL\n");
+               return 0;
+       }
+
+       if (hci->td_array->len != 0) {
+               DBGERR ("ERROR: schedule, hci->td_array->len = 0x%x, s/b: 0\n",
+                       hci->td_array->len);
+       }
+
+       /* schedule the next available interrupt transfer or the next
+        * stage of the interrupt transfer */
+
+       if (hci->td_array->len == 0 && !list_empty (&hci->intr_list)) {
+               units_left = sh_scan_urb_list (hci, &hci->intr_list);
+       }
+
+       /* schedule the next available control transfer or the next
+        * stage of the control transfer */
+
+       if (hci->td_array->len == 0 && !list_empty (&hci->ctrl_list) && units_left > 0) {
+               units_left = sh_scan_urb_list (hci, &hci->ctrl_list);
+       }
+
+       /* schedule the next available bulk transfer or the next
+        * stage of the bulk transfer */
+
+       if (hci->td_array->len == 0 && !list_empty (&hci->bulk_list) && units_left > 0) {
+               sh_scan_urb_list (hci, &hci->bulk_list);
+
+               /* be fair to each BULK URB (move list head around) 
+                * only when the new SOF happens */
+
+               lh = hci->bulk_list.next;
+               list_del (&hci->bulk_list);
+               list_add (&hci->bulk_list, lh);
+       }
+       return 0;
+}
+
+/***************************************************************************
+ * Function Name : sh_add_packet
+ *
+ * This function forms the packet and transmit the packet. This function
+ * will handle all endpoint type: isochoronus, interrupt, control, and 
+ * bulk.
+ * 
+ * Input: hci = data structure for the host controller 
+ *        urb = USB request block data structure 
+ *
+ * Return: 0 = unsucessful; 1 = successful   
+ **************************************************************************/
+static int sh_add_packet (hci_t * hci, struct urb * urb)
+{
+       __u8 *data = NULL;
+       int len = 0;
+       int toggle = 0;
+       int maxps = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe));
+       int endpoint = usb_pipeendpoint (urb->pipe);
+       int address = usb_pipedevice (urb->pipe);
+       int slow = (((urb->pipe) >> 26) & 1);
+       int out = usb_pipeout (urb->pipe);
+       int pid = 0;
+       int ret;
+       int i = 0;
+       int iso = 0;
+
+       DBGFUNC ("enter sh_add_packet\n");
+       if (maxps == 0)
+               maxps = 8;
+
+       /* calculate len, toggle bit and add the transaction */
+       switch (usb_pipetype (urb->pipe)) {
+       case PIPE_ISOCHRONOUS:
+               pid = out ? PID_OUT : PID_IN;
+               iso = 1;
+               i = hci->frame_number - urb->start_frame;
+               data = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+               len = urb->iso_frame_desc[i].length;
+               break;
+
+       case PIPE_BULK: /* BULK and BULK0 */
+       case PIPE_INTERRUPT:
+               pid = out ? PID_OUT : PID_IN;
+               len = urb->transfer_buffer_length - urb->actual_length;
+               data = urb->transfer_buffer + urb->actual_length;
+               toggle = usb_gettoggle (urb->dev, endpoint, out);
+               break;
+
+       case PIPE_CONTROL:
+               switch (qu_urbstate (urb)) {
+               case US_CTRL_SETUP:
+                       len = 8;
+                       pid = PID_SETUP;
+                       data = urb->setup_packet;
+                       toggle = 0;
+                       break;
+
+               case US_CTRL_DATA:
+                       if (!hci->last_packet_nak) {
+                               /* The last packet received is not a nak:
+                                * reset the nak count
+                                */
+
+                               hci->nakCnt = 0;
+                       }
+                       if (urb->transfer_buffer_length != 0) {
+                               pid = out ? PID_OUT : PID_IN;
+                               len = urb->transfer_buffer_length - urb->actual_length;
+                               data = urb->transfer_buffer + urb->actual_length;
+                               toggle = (urb->actual_length & maxps) ? 0 : 1;
+                               usb_settoggle (urb->dev,
+                                              usb_pipeendpoint (urb->pipe),
+                                              usb_pipeout (urb->pipe), toggle);
+                               break;
+                       } else {
+                               /* correct state and fall through */
+                               qu_seturbstate (urb, US_CTRL_ACK);
+                       }
+
+               case US_CTRL_ACK:
+                       len = 0;
+
+                       /* reply in opposite direction */
+                       pid = !out ? PID_OUT : PID_IN;
+                       toggle = 1;
+                       usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe),
+                                      usb_pipeout (urb->pipe), toggle);
+                       break;
+               }
+       }
+
+       ret =
+           hc_add_trans (hci, len, data, toggle, maxps, slow, endpoint,
+                         address, pid, iso, qu_urbstate (urb));
+
+       DBGVERBOSE ("transfer_pa: addr:%d ep:%d pid:%x tog:%x iso:%x sl:%x "
+                   "max:%d\n len:%d ret:%d data:%p left:%d\n",
+                   address, endpoint, pid, toggle, iso, slow,
+                   maxps, len, ret, data, hci->hp.units_left);
+
+       if (ret >= 0) {
+               hci->td_array->td[hci->td_array->len].urb = urb;
+               hci->td_array->td[hci->td_array->len].len = ret;
+               hci->td_array->td[hci->td_array->len].iso_index = i;
+               hci->td_array->len++;
+               hci->active_trans = 1;
+               return 1;
+       }
+       return 0;
+}
+
+/***************************************************************************
+ * Function Name : cc_to_error
+ *
+ * This function maps the SL811HS hardware error code to the linux USB error
+ * code.
+ * 
+ * Input: cc = hardware error code 
+ *
+ * Return: USB error code   
+ **************************************************************************/
+static int cc_to_error (int cc)
+{
+       int errCode = 0;
+       if (cc & SL11H_STATMASK_ERROR) {
+               errCode |= -EILSEQ;
+       } else if (cc & SL11H_STATMASK_OVF) {
+               errCode |= -EOVERFLOW;
+       } else if (cc & SL11H_STATMASK_STALL) {
+               errCode |= -EPIPE;
+       }
+       return errCode;
+}
+
+/***************************************************************************
+ * Function Name : sh_done_list
+ *
+ * This function process the packet when it has done finish transfer.
+ * 
+ * 1) It handles hardware error
+ * 2) It updates the URB state
+ * 3) If the USB transaction is complete, it start the return stack path.
+ * 
+ * Input: hci = data structure for the host controller 
+ *        isExcessNak = flag tells if there excess NAK condition occurred 
+ *
+ * Return:  urb_state or -1 if the transaction has complete   
+ **************************************************************************/
+static int sh_done_list (hci_t * hci, int *isExcessNak)
+{
+       int actbytes = 0;
+       int active = 0;
+       void *data = NULL;
+       int cc;
+       int maxps;
+       int toggle;
+       struct urb *urb;
+       int urb_state = 0;
+       int ret = 1;            /* -1 parse abbort, 1 parse ok, 0 last element */
+       int trans = 0;
+       int len;
+       int iso_index = 0;
+       int out;
+       int pid = 0;
+       int debugLen = 0;
+
+       *isExcessNak = 0;
+
+       DBGFUNC ("enter sh_done_list: td_array->len = 0x%x\n",
+                hci->td_array->len);
+
+       debugLen = hci->td_array->len;
+       if (debugLen > 1)
+               DBGERR ("sh_done_list: td_array->len = 0x%x > 1\n",
+                       hci->td_array->len);
+
+       for (trans = 0; ret && trans < hci->td_array->len && trans < MAX_TRANS;
+            trans++) {
+               urb = hci->td_array->td[trans].urb;
+               len = hci->td_array->td[trans].len;
+               out = usb_pipeout (urb->pipe);
+
+               if (usb_pipeisoc (urb->pipe)) {
+                       iso_index = hci->td_array->td[trans].iso_index;
+                       data = urb->transfer_buffer + urb->iso_frame_desc[iso_index].offset;
+                       toggle = 0;
+               } else {
+                       data = urb->transfer_buffer + urb->actual_length;
+                       toggle = usb_gettoggle (urb->dev,
+                                               usb_pipeendpoint (urb->pipe),
+                                               usb_pipeout (urb->pipe));
+
+               }
+               urb_state = qu_urbstate (urb);
+               pid = out ? PID_OUT : PID_IN;
+               ret = hc_parse_trans (hci, &actbytes, data, &cc, &toggle, len,
+                                     pid, urb_state);
+               maxps = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe));
+
+               if (maxps == 0)
+                       maxps = 8;
+
+               active = (urb_state != US_CTRL_SETUP) && (actbytes && !(actbytes & (maxps - 1)));
+
+               /* If the transfer is not bulk in, then it is necessary to get all
+                * data specify by the urb->transfer_len.
+                */
+
+               if (!(usb_pipebulk (urb->pipe) && usb_pipein (urb->pipe)))
+                       active = active && (urb->transfer_buffer_length != urb->actual_length + actbytes);
+
+               if (urb->transfer_buffer_length == urb->actual_length + actbytes)
+                       active = 0;
+
+               if ((cc &
+                    (SL11H_STATMASK_ERROR | SL11H_STATMASK_TMOUT |
+                     SL11H_STATMASK_OVF | SL11H_STATMASK_STALL))
+                   && !(cc & SL11H_STATMASK_NAK)) {
+                       if (++urb->error_count > 3) {
+                               DBGERR ("done_list: excessive error: errcount = 0x%x, cc = 0x%x\n",
+                                       urb->error_count, cc);
+                               urb_state = 0;
+                               active = 0;
+                       } else {
+                               DBGERR ("done_list: packet err, cc = 0x%x, "
+                                       " urb->length = 0x%x, actual_len = 0x%x,"
+                                       " urb_state =0x%x\n",
+                                       cc, urb->transfer_buffer_length,
+                                       urb->actual_length, urb_state);
+//                     if (cc & SL11H_STATMASK_STALL) {
+                               /* The USB function is STALLED on a control pipe (0), 
+                                * then it needs to send the SETUP command again to 
+                                * clear the STALL condition
+                                */
+
+//                             if (usb_pipeendpoint (urb->pipe) == 0) {
+//                                     urb_state = 2;  
+//                                     active = 0;
+//                             }
+//                     } else   
+                               active = 1;
+                       }
+               } else {
+                       if (cc & SL11H_STATMASK_NAK) {
+                               if (hci->nakCnt < 0x10000) {
+                                       hci->nakCnt++;
+                                       hci->last_packet_nak = 1;
+                                       active = 1;
+                                       *isExcessNak = 0;
+                               } else {
+                                       DBGERR ("done_list: nak count exceed limit\n");
+                                       active = 0;
+                                       *isExcessNak = 1;
+                                       hci->nakCnt = 0;
+                               }
+                       } else {
+                               hci->nakCnt = 0;
+                               hci->last_packet_nak = 0;
+                       }
+
+                       if (urb_state != US_CTRL_SETUP) {
+                               /* no error */
+                               urb->actual_length += actbytes;
+                               usb_settoggle (urb->dev,
+                                              usb_pipeendpoint (urb->pipe),
+                                              usb_pipeout (urb->pipe), toggle);
+                       }
+                       if (usb_pipeisoc (urb->pipe)) {
+                               urb->iso_frame_desc[iso_index].actual_length = actbytes;
+                               urb->iso_frame_desc[iso_index].status = cc_to_error (cc);
+                               active = (iso_index < urb->number_of_packets);
+                       }
+               }
+               if (!active) {
+                       if (!urb_state) {
+                               urb->status = cc_to_error (cc);
+                               if (urb->status) {
+                                       DBGERR ("error on received packet: urb->status = 0x%x\n",
+                                               urb->status);
+                               }
+                               hci->td_array->len = 0;
+                               qu_return_urb (hci, urb, 1);
+                               return -1;
+                       } else {
+                               /* We do not want to decrement the urb_state if exceeded nak,
+                                * because we need to finish the data stage of the control 
+                                * packet 
+                                */
+
+                               if (!(*isExcessNak))
+                                       urb_state--;
+                               qu_seturbstate (urb, urb_state);
+                       }
+               }
+       }
+
+       if (urb_state < 0)
+               DBGERR ("ERROR: done_list, urb_state = %d, suppose > 0\n",
+                       urb_state);
+       if (debugLen != hci->td_array->len) {
+               DBGERR ("ERROR: done_list, debugLen!= td_array->len,"
+                       "debugLen = 0x%x, hci->td_array->len = 0x%x\n",
+                       debugLen, hci->td_array->len);
+       }
+
+       hci->td_array->len = 0;
+
+       return urb_state;
+}
diff --git a/drivers/usb/host/hc_simple.h b/drivers/usb/host/hc_simple.h
new file mode 100644 (file)
index 0000000..dfb67e7
--- /dev/null
@@ -0,0 +1,231 @@
+/*-------------------------------------------------------------------------*/
+/* list of all controllers using this driver 
+ * */
+
+static LIST_HEAD (hci_hcd_list);
+
+/* URB states (urb_state) */
+/* isoc, interrupt single state */
+
+/* bulk transfer main state and 0-length packet */
+#define US_BULK                0
+#define US_BULK0       1
+/* three setup states */
+#define US_CTRL_SETUP  2
+#define US_CTRL_DATA   1
+#define US_CTRL_ACK    0
+
+/*-------------------------------------------------------------------------*/
+/* HC private part of a device descriptor
+ * */
+
+#define NUM_EDS 32
+
+typedef struct epd {
+       struct urb *pipe_head;
+       struct list_head urb_queue;
+//     int urb_state;
+       struct timer_list timeout;
+       int last_iso;           /* timestamp of last queued ISOC transfer */
+
+} epd_t;
+
+struct hci_device {
+       epd_t ed[NUM_EDS];
+};
+
+/*-------------------------------------------------------------------------*/
+/* Virtual Root HUB 
+ */
+
+#define usb_to_hci(usb)        ((struct hci_device *)(usb)->hcpriv)
+
+struct virt_root_hub {
+       int devnum;             /* Address of Root Hub endpoint */
+       void *urb;              /* interrupt URB of root hub */
+       int send;               /* active flag */
+       int interval;           /* intervall of roothub interrupt transfers */
+       struct timer_list rh_int_timer; /* intervall timer for rh interrupt EP */
+};
+
+#if 1
+/* USB HUB CONSTANTS (not OHCI-specific; see hub.h and USB spec) */
+
+/* destination of request */
+#define RH_INTERFACE           0x01
+#define RH_ENDPOINT            0x02
+#define RH_OTHER               0x03
+
+#define RH_CLASS               0x20
+#define RH_VENDOR              0x40
+
+/* Requests: bRequest << 8 | bmRequestType */
+#define RH_GET_STATUS          0x0080
+#define RH_CLEAR_FEATURE       0x0100
+#define RH_SET_FEATURE         0x0300
+#define RH_SET_ADDRESS         0x0500
+#define RH_GET_DESCRIPTOR      0x0680
+#define RH_SET_DESCRIPTOR      0x0700
+#define RH_GET_CONFIGURATION   0x0880
+#define RH_SET_CONFIGURATION   0x0900
+#define RH_GET_STATE           0x0280
+#define RH_GET_INTERFACE       0x0A80
+#define RH_SET_INTERFACE       0x0B00
+#define RH_SYNC_FRAME          0x0C80
+/* Our Vendor Specific Request */
+#define RH_SET_EP              0x2000
+
+/* Hub port features */
+#define RH_PORT_CONNECTION     0x00
+#define RH_PORT_ENABLE         0x01
+#define RH_PORT_SUSPEND                0x02
+#define RH_PORT_OVER_CURRENT   0x03
+#define RH_PORT_RESET          0x04
+#define RH_PORT_POWER          0x08
+#define RH_PORT_LOW_SPEED      0x09
+
+#define RH_C_PORT_CONNECTION   0x10
+#define RH_C_PORT_ENABLE       0x11
+#define RH_C_PORT_SUSPEND      0x12
+#define RH_C_PORT_OVER_CURRENT 0x13
+#define RH_C_PORT_RESET                0x14
+
+/* Hub features */
+#define RH_C_HUB_LOCAL_POWER   0x00
+#define RH_C_HUB_OVER_CURRENT  0x01
+
+#define RH_DEVICE_REMOTE_WAKEUP        0x00
+#define RH_ENDPOINT_STALL      0x01
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+/* struct for each HC 
+ */
+
+#define MAX_TRANS      32
+
+typedef struct td {
+       struct urb *urb;
+       __u16 len;
+       __u16 iso_index;
+} td_t;
+
+typedef struct td_array {
+       int len;
+       td_t td[MAX_TRANS];
+} td_array_t;
+
+typedef struct hci {
+       struct virt_root_hub rh;        /* roothub */
+       wait_queue_head_t waitq;        /* deletion of URBs and devices needs a waitqueue */
+       int active;                     /* HC is operating */
+
+       struct list_head ctrl_list;     /* set of ctrl endpoints */
+       struct list_head bulk_list;     /* set of bulk endpoints */
+       struct list_head iso_list;      /* set of isoc endpoints */
+       struct list_head intr_list;     /* ordered (tree) set of int endpoints */
+       struct list_head del_list;      /* set of entpoints to be deleted */
+
+       td_array_t *td_array;
+       td_array_t a_td_array;
+       td_array_t i_td_array[2];
+
+       struct list_head hci_hcd_list;  /* list of all hci_hcd */
+       struct usb_bus *bus;            /* our bus */
+
+//     int trans;                      /* number of transactions pending */
+       int active_urbs;
+       int active_trans;
+       int frame_number;               /* frame number */
+       hcipriv_t hp;                   /* individual part of hc type */
+       int nakCnt;
+       int last_packet_nak;
+
+} hci_t;
+
+/*-------------------------------------------------------------------------*/
+/* condition (error) CC codes and mapping OHCI like
+ */
+
+#define TD_CC_NOERROR          0x00
+#define TD_CC_CRC              0x01
+#define TD_CC_BITSTUFFING      0x02
+#define TD_CC_DATATOGGLEM      0x03
+#define TD_CC_STALL            0x04
+#define TD_DEVNOTRESP          0x05
+#define TD_PIDCHECKFAIL                0x06
+#define TD_UNEXPECTEDPID       0x07
+#define TD_DATAOVERRUN         0x08
+#define TD_DATAUNDERRUN                0x09
+#define TD_BUFFEROVERRUN       0x0C
+#define TD_BUFFERUNDERRUN      0x0D
+#define TD_NOTACCESSED         0x0F
+
+
+/* urb interface functions */
+static int hci_get_current_frame_number (struct usb_device *usb_dev);
+static int hci_unlink_urb (struct urb * urb);
+
+static int qu_queue_urb (hci_t * hci, struct urb * urb);
+
+/* root hub */
+static int rh_init_int_timer (struct urb * urb);
+static int rh_submit_urb (struct urb * urb);
+static int rh_unlink_urb (struct urb * urb);
+
+/* schedule functions */
+static int sh_add_packet (hci_t * hci, struct urb * urb);
+
+/* hc specific functions */
+static inline void hc_flush_data_cache (hci_t * hci, void *data, int len);
+static inline int hc_parse_trans (hci_t * hci, int *actbytes, __u8 * data,
+                                 int *cc, int *toggle, int length, int pid,
+                                 int urb_state);
+static inline int hc_add_trans (hci_t * hci, int len, void *data, int toggle,
+                               int maxps, int slow, int endpoint, int address,
+                               int pid, int format, int urb_state);
+
+static void hc_start_int (hci_t * hci);
+static void hc_stop_int (hci_t * hci);
+static void SL811Write (hci_t * hci, char offset, char data);
+
+/* debug| print the main components of an URB     
+ * small: 0) header + data packets 1) just header */
+
+static void urb_print (struct urb * urb, char *str, int small)
+{
+       unsigned int pipe = urb->pipe;
+       int i, len;
+
+       if (!urb->dev || !urb->dev->bus) {
+               dbg ("%s URB: no dev", str);
+               return;
+       }
+
+       printk ("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)\n",
+               str, hci_get_current_frame_number (urb->dev),
+               usb_pipedevice (pipe), usb_pipeendpoint (pipe),
+               usb_pipeout (pipe) ? 'O' : 'I',
+               usb_pipetype (pipe) < 2 ? (usb_pipeint (pipe) ? "INTR" : "ISOC")
+               : (usb_pipecontrol (pipe) ? "CTRL" : "BULK"), urb->transfer_flags,
+               urb->actual_length, urb->transfer_buffer_length, urb->status,
+               urb->status);
+       if (!small) {
+               if (usb_pipecontrol (pipe)) {
+                       printk (__FILE__ ": cmd(8):");
+                       for (i = 0; i < 8; i++)
+                               printk (" %02x", ((__u8 *) urb->setup_packet)[i]);
+                       printk ("\n");
+               }
+               if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) {
+                       printk (__FILE__ ": data(%d/%d):", urb->actual_length,
+                               urb->transfer_buffer_length);
+                       len = usb_pipeout (pipe) ? urb-> transfer_buffer_length : urb->actual_length;
+                       for (i = 0; i < 2096 && i < len; i++)
+                               printk (" %02x", ((__u8 *) urb->transfer_buffer)[i]);
+                       printk ("%s stat:%d\n", i < len ? "..." : "",
+                               urb->status);
+               }
+       }
+}
diff --git a/drivers/usb/host/hc_sl811.c b/drivers/usb/host/hc_sl811.c
new file mode 100644 (file)
index 0000000..3df1c3e
--- /dev/null
@@ -0,0 +1,1358 @@
+/*-------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------*
+ * SL811HS USB HCD for Linux Version 0.1 (10/28/2001)
+ * 
+ * requires (includes) hc_simple.[hc] simple generic HCD frontend
+ *  
+ * COPYRIGHT(C) 2001 by CYPRESS SEMICONDUCTOR INC.
+ *
+ *-------------------------------------------------------------------------*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *-------------------------------------------------------------------------*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/list.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <linux/usb.h>
+#include "../core/hcd.h"
+
+#undef HC_URB_TIMEOUT
+#undef HC_SWITCH_INT
+#undef HC_ENABLE_ISOC
+
+#define SL811_DEBUG_ERR
+
+#ifdef SL811_DEBUG_ERR
+#define DBGERR(fmt, args...) printk(fmt,## args)
+#else
+#define DBGERR(fmt, args...)
+#endif
+
+#ifdef SL811_DEBUG
+#define DBG(fmt, args...) printk(fmt,## args)
+#else
+#define DBG(fmt, args...)
+#endif
+
+#ifdef SL811_DEBUG_FUNC
+#define DBGFUNC(fmt, args...) printk(fmt,## args)
+#else
+#define DBGFUNC(fmt, args...)
+#endif
+
+#ifdef SL811_DEBUG_DATA
+#define DBGDATAR(fmt, args...) printk(fmt,## args)
+#define DBGDATAW(fmt, args...) printk(fmt,## args)
+#else
+#define DBGDATAR(fmt, args...)
+#define DBGDATAW(fmt, args...)
+#endif
+
+#ifdef SL811_DEBUG_VERBOSE
+#define DBGVERBOSE(fmt, args...) printk(fmt,## args)
+#else
+#define DBGVERBOSE(fmt, args...)
+#endif
+
+#define TRUE 1
+#define FALSE 0
+
+#define HC_SWITCH_INT
+#include "hc_sl811.h"
+#include "hc_simple.h"
+
+static int urb_debug = 0;
+
+#include "hc_simple.c"
+#include "hc_sl811_rh.c"
+
+/* The base_addr, data_reg_addr, and irq number are board specific.
+ * The current values are design to run on the Accelent SA1110 IDP
+ * NOTE: values need to modify for different development boards 
+ */
+
+static int base_addr = 0xd3800000;
+static int data_reg_addr = 0xd3810000;
+static int irq = 34;
+
+/* forware declaration */
+
+int SL11StartXaction (hci_t * hci, __u8 addr, __u8 epaddr, int pid, int len,
+                     int toggle, int slow, int urb_state);
+
+static int sofWaitCnt = 0;
+
+MODULE_PARM (urb_debug, "i");
+MODULE_PARM_DESC (urb_debug, "debug urb messages, default is 0 (no)");
+
+MODULE_PARM (base_addr, "i");
+MODULE_PARM_DESC (base_addr, "sl811 base address 0xd3800000");
+MODULE_PARM (data_reg_addr, "i");
+MODULE_PARM_DESC (data_reg_addr, "sl811 data register address 0xd3810000");
+MODULE_PARM (irq, "i");
+MODULE_PARM_DESC (irq, "IRQ 34 (default)");
+
+static int hc_reset (hci_t * hci);
+
+/***************************************************************************
+ * Function Name : SL811Read
+ *
+ * Read a byte of data from the SL811H/SL11H
+ *
+ * Input:  hci = data structure for the host controller
+ *         offset = address of SL811/SL11H register or memory
+ *
+ * Return: data 
+ **************************************************************************/
+char SL811Read (hci_t * hci, char offset)
+{
+       hcipriv_t *hp = &hci->hp;
+       char data;
+       writeb (offset, hp->hcport);
+       wmb ();
+       data = readb (hp->hcport2);
+       rmb ();
+       return (data);
+}
+
+/***************************************************************************
+ * Function Name : SL811Write
+ *
+ * Write a byte of data to the SL811H/SL11H
+ *
+ * Input:  hci = data structure for the host controller
+ *         offset = address of SL811/SL11H register or memory
+ *         data  = the data going to write to SL811H
+ *
+ * Return: none 
+ **************************************************************************/
+void SL811Write (hci_t * hci, char offset, char data)
+{
+       hcipriv_t *hp = &hci->hp;
+       writeb (offset, hp->hcport);
+       writeb (data, hp->hcport2);
+       wmb ();
+}
+
+/***************************************************************************
+ * Function Name : SL811BufRead
+ *
+ * Read consecutive bytes of data from the SL811H/SL11H buffer
+ *
+ * Input:  hci = data structure for the host controller
+ *         offset = SL811/SL11H register offset
+ *         buf = the buffer where the data will store
+ *         size = number of bytes to read
+ *
+ * Return: none 
+ **************************************************************************/
+void SL811BufRead (hci_t * hci, short offset, char *buf, short size)
+{
+       hcipriv_t *hp = &hci->hp;
+       if (size <= 0)
+               return;
+       writeb ((char) offset, hp->hcport);
+       wmb ();
+       DBGDATAR ("SL811BufRead: offset = 0x%x, data = ", offset);
+       while (size--) {
+               *buf++ = (char) readb (hp->hcport2);
+               DBGDATAR ("0x%x ", *(buf - 1));
+               rmb ();
+       }
+       DBGDATAR ("\n");
+}
+
+/***************************************************************************
+ * Function Name : SL811BufWrite
+ *
+ * Write consecutive bytes of data to the SL811H/SL11H buffer
+ *
+ * Input:  hci = data structure for the host controller
+ *         offset = SL811/SL11H register offset
+ *         buf = the data buffer 
+ *         size = number of bytes to write
+ *
+ * Return: none 
+ **************************************************************************/
+void SL811BufWrite (hci_t * hci, short offset, char *buf, short size)
+{
+       hcipriv_t *hp = &hci->hp;
+       if (size <= 0)
+               return;
+       writeb ((char) offset, hp->hcport);
+       wmb ();
+       DBGDATAW ("SL811BufWrite: offset = 0x%x, data = ", offset);
+       while (size--) {
+               DBGDATAW ("0x%x ", *buf);
+               writeb (*buf, hp->hcport2);
+               wmb ();
+               buf++;
+       }
+       DBGDATAW ("\n");
+}
+
+/***************************************************************************
+ * Function Name : regTest
+ *
+ * This routine test the Read/Write functionality of SL811HS registers  
+ *
+ * 1) Store original register value into a buffer
+ * 2) Write to registers with a RAMP pattern. (10, 11, 12, ..., 255)
+ * 3) Read from register
+ * 4) Compare the written value with the read value and make sure they are 
+ *    equivalent
+ * 5) Restore the original register value 
+ *
+ * Input:  hci = data structure for the host controller
+ *   
+ *
+ * Return: TRUE = passed; FALSE = failed 
+ **************************************************************************/
+int regTest (hci_t * hci)
+{
+       int i, data, result = TRUE;
+       char buf[256];
+
+       DBGFUNC ("Enter regTest\n");
+       for (i = 0x10; i < 256; i++) {
+               /* save the original buffer */
+               buf[i] = (char) SL811Read (hci, i);
+
+               /* Write the new data to the buffer */
+               SL811Write (hci, i, i);
+       }
+
+       /* compare the written data */
+       for (i = 0x10; i < 256; i++) {
+               data = SL811Read (hci, i);
+               if (data != i) {
+                       DBGERR ("Pattern test failed!! value = 0x%x, s/b 0x%x\n",
+                               data, i);
+                       result = FALSE;
+               }
+       }
+
+       /* restore the data */
+       for (i = 0x10; i < 256; i++) {
+               SL811Write (hci, i, buf[i]);
+       }
+
+       return (result);
+}
+
+/***************************************************************************
+ * Function Name : regShow
+ *
+ * Display all SL811HS register values
+ *
+ * Input:  hci = data structure for the host controller
+ *
+ * Return: none 
+ **************************************************************************/
+void regShow (hci_t * hci)
+{
+       int i;
+       for (i = 0; i < 256; i++) {
+               printk ("offset %d: 0x%x\n", i, SL811Read (hci, i));
+       }
+}
+
+/************************************************************************
+ * Function Name : USBReset
+ *  
+ * This function resets SL811HS controller and detects the speed of
+ * the connecting device                                 
+ *
+ * Input:  hci = data structure for the host controller
+ *                
+ * Return: 0 = no device attached; 1 = USB device attached
+ *                
+ ***********************************************************************/
+static int USBReset (hci_t * hci)
+{
+       int status;
+       hcipriv_t *hp = &hci->hp;
+
+       DBGFUNC ("enter USBReset\n");
+
+       SL811Write (hci, SL11H_CTLREG2, 0xae);
+
+       // setup master and full speed
+
+       SL811Write (hci, SL11H_CTLREG1, 0x08);  // reset USB
+       mdelay (20);            // 20ms                             
+       SL811Write (hci, SL11H_CTLREG1, 0);     // remove SE0        
+
+       for (status = 0; status < 100; status++)
+               SL811Write (hci, SL11H_INTSTATREG, 0xff);       // clear all interrupt bits
+
+       status = SL811Read (hci, SL11H_INTSTATREG);
+
+       if (status & 0x40)      // Check if device is removed
+       {
+               DBG ("USBReset: Device removed\n");
+               SL811Write (hci, SL11H_INTENBLREG,
+                           SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR |
+                           SL11H_INTMASK_INSRMV);
+               hp->RHportStatus->portStatus &=
+                   ~(PORT_CONNECT_STAT | PORT_ENABLE_STAT);
+
+               return 0;
+       }
+
+       SL811Write (hci, SL11H_BUFLNTHREG_B, 0);        //zero lenth
+       SL811Write (hci, SL11H_PIDEPREG_B, 0x50);       //send SOF to EP0       
+       SL811Write (hci, SL11H_DEVADDRREG_B, 0x01);     //address0
+       SL811Write (hci, SL11H_SOFLOWREG, 0xe0);
+
+       if (!(status & 0x80)) {
+               /* slow speed device connect directly to root-hub */
+
+               DBG ("USBReset: low speed Device attached\n");
+               SL811Write (hci, SL11H_CTLREG1, 0x8);
+               mdelay (20);
+               SL811Write (hci, SL11H_SOFTMRREG, 0xee);
+               SL811Write (hci, SL11H_CTLREG1, 0x21);
+
+               /* start the SOF or EOP */
+
+               SL811Write (hci, SL11H_HOSTCTLREG_B, 0x01);
+               hp->RHportStatus->portStatus |=
+                   (PORT_CONNECT_STAT | PORT_LOW_SPEED_DEV_ATTACH_STAT);
+
+               /* clear all interrupt bits */
+
+               for (status = 0; status < 20; status++)
+                       SL811Write (hci, SL11H_INTSTATREG, 0xff);
+       } else {
+               /* full speed device connect directly to root hub */
+
+               DBG ("USBReset: full speed Device attached\n");
+               SL811Write (hci, SL11H_CTLREG1, 0x8);
+               mdelay (20);
+               SL811Write (hci, SL11H_SOFTMRREG, 0xae);
+               SL811Write (hci, SL11H_CTLREG1, 0x01);
+
+               /* start the SOF or EOP */
+
+               SL811Write (hci, SL11H_HOSTCTLREG_B, 0x01);
+               hp->RHportStatus->portStatus |= (PORT_CONNECT_STAT);
+               hp->RHportStatus->portStatus &= ~PORT_LOW_SPEED_DEV_ATTACH_STAT;
+
+               /* clear all interrupt bits */
+
+               SL811Write (hci, SL11H_INTSTATREG, 0xff);
+
+       }
+
+       /* enable all interrupts */
+       SL811Write (hci, SL11H_INTENBLREG,
+                   SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR |
+                   SL11H_INTMASK_INSRMV);
+
+       return 1;
+}
+
+/*-------------------------------------------------------------------------*/
+/* tl functions */
+static inline void hc_mark_last_trans (hci_t * hci)
+{
+       hcipriv_t *hp = &hci->hp;
+       __u8 *ptd = hp->tl;
+
+       dbg ("enter hc_mark_last_trans\n");
+       if (ptd == NULL) {
+               printk ("hc_mark_last_trans: ptd = null\n");
+               return;
+       }
+       if (hp->xferPktLen > 0)
+               *(ptd + hp->tl_last) |= (1 << 3);
+}
+
+static inline void hc_flush_data_cache (hci_t * hci, void *data, int len)
+{
+}
+
+/************************************************************************
+ * Function Name : hc_add_trans
+ *  
+ * This function sets up the SL811HS register and transmit the USB packets.
+ * 
+ * 1) Determine if enough time within the current frame to send the packet
+ * 2) Load the data into the SL811HS register
+ * 3) Set the appropriate command to the register and trigger the transmit
+ *
+ * Input:  hci = data structure for the host controller
+ *         len = data length
+ *         data = transmitting data
+ *         toggle = USB toggle bit, either 0 or 1
+ *         maxps = maximum packet size for this endpoint
+ *         slow = speed of the device
+ *         endpoint = endpoint number
+ *         address = USB address of the device
+ *         pid = packet ID
+ *         format = 
+ *         urb_state = the current stage of USB transaction
+ *       
+ * Return: 0 = no time left to schedule the transfer
+ *         1 = success 
+ *                
+ ***********************************************************************/
+static inline int hc_add_trans (hci_t * hci, int len, void *data, int toggle,
+                               int maxps, int slow, int endpoint, int address,
+                               int pid, int format, int urb_state)
+{
+       hcipriv_t *hp = &hci->hp;
+       __u16 speed;
+       int ii, jj, kk;
+
+       DBGFUNC ("enter hc_addr_trans: len =0x%x, toggle:0x%x, endpoing:0x%x,"
+                " addr:0x%x, pid:0x%x,format:0x%x\n", len, toggle, endpoint,
+                i address, pid, format);
+
+       if (len > maxps) {
+               len = maxps;
+       }
+
+       speed = hp->RHportStatus->portStatus;
+       if (speed & PORT_LOW_SPEED_DEV_ATTACH_STAT) {
+//      ii = (8*7*8 + 6*3) * len + 800; 
+               ii = 8 * 8 * len + 1024;
+       } else {
+               if (slow) {
+//          ii = (8*7*8 + 6*3) * len + 800; 
+                       ii = 8 * 8 * len + 2048;
+               } else
+//          ii = (8*7 + 6*3)*len + 110;
+                       ii = 8 * len + 256;
+       }
+
+       ii += 2 * 10 * len;
+
+       jj = SL811Read (hci, SL11H_SOFTMRREG);
+       kk = (jj & 0xFF) * 64 - ii;
+
+       if (kk < 0) {
+               DBGVERBOSE
+                   ("hc_add_trans: no bandwidth for schedule, ii = 0x%x,"
+                    "jj = 0x%x, len =0x%x, active_trans = 0x%x\n", ii, jj, len,
+                    hci->active_trans);
+               return (-1);
+       }
+
+       if (pid != PID_IN) {
+               /* Load data into hc */
+
+               SL811BufWrite (hci, SL11H_DATA_START, (__u8 *) data, len);
+       }
+
+       /* transmit */
+
+       SL11StartXaction (hci, (__u8) address, (__u8) endpoint, (__u8) pid, len,
+                         toggle, slow, urb_state);
+
+       return len;
+}
+
+/************************************************************************
+ * Function Name : hc_parse_trans
+ *  
+ * This function checks the status of the transmitted or received packet
+ * and copy the data from the SL811HS register into a buffer.
+ *
+ * 1) Check the status of the packet 
+ * 2) If successful, and IN packet then copy the data from the SL811HS register
+ *    into a buffer
+ *
+ * Input:  hci = data structure for the host controller
+ *         actbytes = pointer to actual number of bytes
+ *         data = data buffer
+ *         cc = packet status
+ *         length = the urb transmit length
+ *         pid = packet ID
+ *         urb_state = the current stage of USB transaction
+ *       
+ * Return: 0 
+ ***********************************************************************/
+static inline int hc_parse_trans (hci_t * hci, int *actbytes, __u8 * data,
+                                 int *cc, int *toggle, int length, int pid,
+                                 int urb_state)
+{
+       __u8 addr;
+       __u8 len;
+
+       DBGFUNC ("enter hc_parse_trans\n");
+
+       /* get packet status; convert ack rcvd to ack-not-rcvd */
+
+       *cc = (int) SL811Read (hci, SL11H_PKTSTATREG);
+
+       if (*cc &
+           (SL11H_STATMASK_ERROR | SL11H_STATMASK_TMOUT | SL11H_STATMASK_OVF |
+            SL11H_STATMASK_NAK | SL11H_STATMASK_STALL)) {
+               if (*cc & SL11H_STATMASK_OVF)
+                       DBGERR ("parse trans: error recv ack, cc = 0x%x, TX_BASE_Len = "
+                               "0x%x, TX_count=0x%x\n", *cc,
+                               SL811Read (hci, SL11H_BUFLNTHREG),
+                               SL811Read (hci, SL11H_XFERCNTREG));
+
+       } else {
+               DBGVERBOSE ("parse trans: recv ack, cc = 0x%x, len = 0x%x, \n",
+                           *cc, length);
+
+               /* Successful data */
+               if ((pid == PID_IN) && (urb_state != US_CTRL_SETUP)) {
+
+                       /* Find the base address */
+                       addr = SL811Read (hci, SL11H_BUFADDRREG);
+
+                       /* Find the Transmit Length */
+                       len = SL811Read (hci, SL11H_BUFLNTHREG);
+
+                       /* The actual data length = xmit length reg - xfer count reg */
+                       *actbytes = len - SL811Read (hci, SL11H_XFERCNTREG);
+
+                       if ((data != NULL) && (*actbytes > 0)) {
+                               SL811BufRead (hci, addr, data, *actbytes);
+
+                       } else if ((data == NULL) && (*actbytes <= 0)) {
+                               DBGERR ("hc_parse_trans: data = NULL or actbyte = 0x%x\n",
+                                       *actbytes);
+                               return 0;
+                       }
+               } else if (pid == PID_OUT) {
+                       *actbytes = length;
+               } else {
+                       // printk ("ERR:parse_trans, pid != IN or OUT, pid = 0x%x\n", pid);
+               }
+               *toggle = !*toggle;
+       }
+
+       return 0;
+}
+
+/************************************************************************
+ * Function Name : hc_start_int
+ *  
+ * This function enables SL811HS interrupts
+ *
+ * Input:  hci = data structure for the host controller
+ *       
+ * Return: none 
+ ***********************************************************************/
+static void hc_start_int (hci_t * hci)
+{
+#ifdef HC_SWITCH_INT
+       int mask =
+           SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR |
+           SL11H_INTMASK_INSRMV | SL11H_INTMASK_USBRESET;
+       SL811Write (hci, IntEna, mask);
+#endif
+}
+
+/************************************************************************
+ * Function Name : hc_stop_int
+ *  
+ * This function disables SL811HS interrupts
+ *
+ * Input:  hci = data structure for the host controller
+ *       
+ * Return: none 
+ ***********************************************************************/
+static void hc_stop_int (hci_t * hci)
+{
+#ifdef HC_SWITCH_INT
+       SL811Write (hci, SL11H_INTSTATREG, 0xff);
+//  SL811Write(hci, SL11H_INTENBLREG, SL11H_INTMASK_INSRMV);
+
+#endif
+}
+
+/************************************************************************
+ * Function Name : handleInsRmvIntr
+ *  
+ * This function handles the insertion or removal of device on  SL811HS. 
+ * It resets the controller and updates the port status
+ *
+ * Input:  hci = data structure for the host controller
+ *       
+ * Return: none 
+ ***********************************************************************/
+void handleInsRmvIntr (hci_t * hci)
+{
+       hcipriv_t *hp = &hci->hp;
+
+       USBReset (hci);
+
+       /* Changes in connection status */
+
+       hp->RHportStatus->portChange |= PORT_CONNECT_CHANGE;
+
+       /* Port Enable or Disable */
+
+       if (hp->RHportStatus->portStatus & PORT_CONNECT_STAT) {
+               /* device is connected to the port:
+                *    1) Enable port 
+                *    2) Resume ?? 
+                */
+//               hp->RHportStatus->portChange |= PORT_ENABLE_CHANGE;
+
+               /* Over Current is not supported by the SL811 HW ?? */
+
+               /* How about the Port Power ?? */
+
+       } else {
+               /* Device has disconnect:
+                *    1) Disable port
+                */
+
+               hp->RHportStatus->portStatus &= ~(PORT_ENABLE_STAT);
+               hp->RHportStatus->portChange |= PORT_ENABLE_CHANGE;
+
+       }
+}
+
+/*****************************************************************
+ *
+ * Function Name: SL11StartXaction
+ *  
+ * This functions load the registers with appropriate value and 
+ * transmit the packet.                                  
+ *
+ * Input:  hci = data structure for the host controller
+ *         addr = USB address of the device
+ *         epaddr = endpoint number
+ *         pid = packet ID
+ *         len = data length
+ *         toggle = USB toggle bit, either 0 or 1
+ *         slow = speed of the device
+ *         urb_state = the current stage of USB transaction
+ *
+ * Return: 0 = error; 1 = successful
+ *                
+ *****************************************************************/
+int SL11StartXaction (hci_t * hci, __u8 addr, __u8 epaddr, int pid, int len,
+                     int toggle, int slow, int urb_state)
+{
+
+       hcipriv_t *hp = &hci->hp;
+       __u8 cmd = 0;
+       __u8 setup_data[4];
+       __u16 speed;
+
+       speed = hp->RHportStatus->portStatus;
+       if (!(speed & PORT_LOW_SPEED_DEV_ATTACH_STAT) && slow) {
+               cmd |= SL11H_HCTLMASK_PREAMBLE;
+       }
+       switch (pid) {
+       case PID_SETUP:
+               cmd &= SL11H_HCTLMASK_PREAMBLE;
+               cmd |=
+                   (SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENBLEP |
+                    SL11H_HCTLMASK_WRITE);
+               break;
+
+       case PID_OUT:
+               cmd &= (SL11H_HCTLMASK_SEQ | SL11H_HCTLMASK_PREAMBLE);
+               cmd |=
+                   (SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENBLEP |
+                    SL11H_HCTLMASK_WRITE);
+               if (toggle) {
+                       cmd |= SL11H_HCTLMASK_SEQ;
+               }
+               break;
+
+       case PID_IN:
+               cmd &= (SL11H_HCTLMASK_SEQ | SL11H_HCTLMASK_PREAMBLE);
+               cmd |= (SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENBLEP);
+               break;
+
+       default:
+               DBGERR ("ERR: SL11StartXaction: unknow pid = 0x%x\n", pid);
+               return 0;
+       }
+       setup_data[0] = SL11H_DATA_START;
+       setup_data[1] = len;
+       setup_data[2] = (((pid & 0x0F) << 4) | (epaddr & 0xF));
+       setup_data[3] = addr & 0x7F;
+
+       SL811BufWrite (hci, SL11H_BUFADDRREG, (__u8 *) & setup_data[0], 4);
+
+       SL811Write (hci, SL11H_HOSTCTLREG, cmd);
+
+#if 0
+       /* The SL811 has a hardware flaw when hub devices sends out
+        * SE0 between packets. It has been found in a TI chipset and
+        * cypress hub chipset. It causes the SL811 to hang
+        * The workaround is to re-issue the preample again.
+        */
+
+       if ((cmd & SL11H_HCTLMASK_PREAMBLE)) {
+               SL811Write (hci, SL11H_PIDEPREG_B, 0xc0);
+               SL811Write (hci, SL11H_HOSTCTLREG_B, 0x1);      // send the premable
+       }
+#endif
+       return 1;
+}
+
+/*****************************************************************
+ *
+ * Function Name: hc_interrupt
+ *
+ * Interrupt service routine. 
+ *
+ * 1) determine the causes of interrupt
+ * 2) clears all interrupts
+ * 3) calls appropriate function to service the interrupt
+ *
+ * Input:  irq = interrupt line associated with the controller 
+ *         hci = data structure for the host controller
+ *         r = holds the snapshot of the processor's context before 
+ *             the processor entered interrupt code. (not used here) 
+ *
+ * Return value  : None.
+ *                
+ *****************************************************************/
+static void hc_interrupt (int irq, void *__hci, struct pt_regs *r)
+{
+       char ii;
+       hci_t *hci = __hci;
+       int isExcessNak = 0;
+       int urb_state = 0;
+       char tmpIrq = 0;
+
+       /* Get value from interrupt status register */
+
+       ii = SL811Read (hci, SL11H_INTSTATREG);
+
+       if (ii & SL11H_INTMASK_INSRMV) {
+               /* Device insertion or removal detected for the USB port */
+
+               SL811Write (hci, SL11H_INTENBLREG, 0);
+               SL811Write (hci, SL11H_CTLREG1, 0);
+               mdelay (100);   // wait for device stable 
+               handleInsRmvIntr (hci);
+               return;
+       }
+
+       /* Clear all interrupts */
+
+       SL811Write (hci, SL11H_INTSTATREG, 0xff);
+
+       if (ii & SL11H_INTMASK_XFERDONE) {
+               /* USB Done interrupt occurred */
+
+               urb_state = sh_done_list (hci, &isExcessNak);
+#ifdef WARNING
+               if (hci->td_array->len > 0)
+                       printk ("WARNING: IRQ, td_array->len = 0x%x, s/b:0\n",
+                               hci->td_array->len);
+#endif
+               if (hci->td_array->len == 0 && !isExcessNak
+                   && !(ii & SL11H_INTMASK_SOFINTR) && (urb_state == 0)) {
+                       if (urb_state == 0) {
+                               /* All urb_state has not been finished yet! 
+                                * continue with the current urb transaction 
+                                */
+
+                               if (hci->last_packet_nak == 0) {
+                                       if (!usb_pipecontrol
+                                           (hci->td_array->td[0].urb->pipe))
+                                               sh_add_packet (hci, hci->td_array-> td[0].urb);
+                               }
+                       } else {
+                               /* The last transaction has completed:
+                                * schedule the next transaction 
+                                */
+
+                               sh_schedule_trans (hci, 0);
+                       }
+               }
+               SL811Write (hci, SL11H_INTSTATREG, 0xff);
+               return;
+       }
+
+       if (ii & SL11H_INTMASK_SOFINTR) {
+               hci->frame_number = (hci->frame_number + 1) % 2048;
+               if (hci->td_array->len == 0)
+                       sh_schedule_trans (hci, 1);
+               else {
+                       if (sofWaitCnt++ > 100) {
+                               /* The last transaction has not completed.
+                                * Need to retire the current td, and let
+                                * it transmit again later on.
+                                * (THIS NEEDS TO BE WORK ON MORE, IT SHOULD NEVER 
+                                *  GET TO THIS POINT)
+                                */
+
+                               DBGERR ("SOF interrupt: td_array->len = 0x%x, s/b: 0\n",
+                                       hci->td_array->len);
+                               urb_print (hci->td_array->td[hci->td_array->len - 1].urb,
+                                          "INTERRUPT", 0);
+                               sh_done_list (hci, &isExcessNak);
+                               SL811Write (hci, SL11H_INTSTATREG, 0xff);
+                               hci->td_array->len = 0;
+                               sofWaitCnt = 0;
+                       }
+               }
+               tmpIrq = SL811Read (hci, SL11H_INTSTATREG) & SL811Read (hci, SL11H_INTENBLREG);
+               if (tmpIrq) {
+                       DBG ("IRQ occurred while service SOF: irq = 0x%x\n",
+                            tmpIrq);
+
+                       /* If we receive a DONE IRQ after schedule, need to 
+                        * handle DONE IRQ again 
+                        */
+
+                       if (tmpIrq & SL11H_INTMASK_XFERDONE) {
+                               DBGERR ("IRQ occurred while service SOF: irq = 0x%x\n",
+                                       tmpIrq);
+                               urb_state = sh_done_list (hci, &isExcessNak);
+                       }
+                       SL811Write (hci, SL11H_INTSTATREG, 0xff);
+               }
+       } else {
+               DBG ("SL811 ISR: unknown, int = 0x%x \n", ii);
+       }
+
+       SL811Write (hci, SL11H_INTSTATREG, 0xff);
+       return;
+}
+
+/*****************************************************************
+ *
+ * Function Name: hc_reset
+ *
+ * This function does register test and resets the SL811HS 
+ * controller.
+ *
+ * Input:  hci = data structure for the host controller
+ *
+ * Return value  : 0
+ *                
+ *****************************************************************/
+static int hc_reset (hci_t * hci)
+{
+       int attachFlag = 0;
+
+       DBGFUNC ("Enter hc_reset\n");
+       regTest (hci);
+       attachFlag = USBReset (hci);
+       if (attachFlag) {
+               setPortChange (hci, PORT_CONNECT_CHANGE);
+       }
+       return (0);
+}
+
+/*****************************************************************
+ *
+ * Function Name: hc_alloc_trans_buffer
+ *
+ * This function allocates all transfer buffer  
+ *
+ * Input:  hci = data structure for the host controller
+ *
+ * Return value  : 0
+ *                
+ *****************************************************************/
+static int hc_alloc_trans_buffer (hci_t * hci)
+{
+       hcipriv_t *hp = &hci->hp;
+       int maxlen;
+
+       hp->itl0_len = 0;
+       hp->itl1_len = 0;
+       hp->atl_len = 0;
+
+       hp->itl_buffer_len = 1024;
+       hp->atl_buffer_len = 4096 - 2 * hp->itl_buffer_len;     /* 2048 */
+
+       maxlen = (hp->itl_buffer_len > hp->atl_buffer_len) ? hp->itl_buffer_len : hp->atl_buffer_len;
+
+       hp->tl = kmalloc (maxlen, GFP_KERNEL);
+
+       if (!hp->tl)
+               return -ENOMEM;
+
+       memset (hp->tl, 0, maxlen);
+       return 0;
+}
+
+/*****************************************************************
+ *
+ * Function Name: getPortStatusAndChange
+ *
+ * This function gets the ports status from SL811 and format it 
+ * to a USB request format
+ *
+ * Input:  hci = data structure for the host controller
+ *
+ * Return value  : port status and change
+ *                
+ *****************************************************************/
+static __u32 getPortStatusAndChange (hci_t * hci)
+{
+       hcipriv_t *hp = &hci->hp;
+       __u32 portstatus;
+
+       DBGFUNC ("enter getPorStatusAndChange\n");
+
+       portstatus = hp->RHportStatus->portChange << 16 | hp->RHportStatus->portStatus;
+
+       return (portstatus);
+}
+
+/*****************************************************************
+ *
+ * Function Name: setPortChange
+ *
+ * This function set the bit position of portChange.
+ *
+ * Input:  hci = data structure for the host controller
+ *         bitPos = the bit position
+ *
+ * Return value  : none 
+ *                
+ *****************************************************************/
+static void setPortChange (hci_t * hci, __u16 bitPos)
+{
+       hcipriv_t *hp = &hci->hp;
+
+       switch (bitPos) {
+       case PORT_CONNECT_STAT:
+               hp->RHportStatus->portChange |= bitPos;
+               break;
+
+       case PORT_ENABLE_STAT:
+               hp->RHportStatus->portChange |= bitPos;
+               break;
+
+       case PORT_RESET_STAT:
+               hp->RHportStatus->portChange |= bitPos;
+               break;
+
+       case PORT_POWER_STAT:
+               hp->RHportStatus->portChange |= bitPos;
+               break;
+
+       case PORT_SUSPEND_STAT:
+               hp->RHportStatus->portChange |= bitPos;
+               break;
+
+       case PORT_OVER_CURRENT_STAT:
+               hp->RHportStatus->portChange |= bitPos;
+               break;
+       }
+}
+
+/*****************************************************************
+ *
+ * Function Name: clrPortChange
+ *
+ * This function clear the bit position of portChange.
+ *
+ * Input:  hci = data structure for the host controller
+ *         bitPos = the bit position
+ *
+ * Return value  : none 
+ *                
+ *****************************************************************/
+static void clrPortChange (hci_t * hci, __u16 bitPos)
+{
+       hcipriv_t *hp = &hci->hp;
+       switch (bitPos) {
+       case PORT_CONNECT_CHANGE:
+               hp->RHportStatus->portChange &= ~bitPos;
+               break;
+
+       case PORT_ENABLE_CHANGE:
+               hp->RHportStatus->portChange &= ~bitPos;
+               break;
+
+       case PORT_RESET_CHANGE:
+               hp->RHportStatus->portChange &= ~bitPos;
+               break;
+
+       case PORT_SUSPEND_CHANGE:
+               hp->RHportStatus->portChange &= ~bitPos;
+               break;
+
+       case PORT_OVER_CURRENT_CHANGE:
+               hp->RHportStatus->portChange &= ~bitPos;
+               break;
+       }
+}
+
+/*****************************************************************
+ *
+ * Function Name: clrPortStatus
+ *
+ * This function clear the bit position of portStatus.
+ *
+ * Input:  hci = data structure for the host controller
+ *         bitPos = the bit position
+ *
+ * Return value  : none 
+ *                
+ *****************************************************************/
+static void clrPortStatus (hci_t * hci, __u16 bitPos)
+{
+       hcipriv_t *hp = &hci->hp;
+       switch (bitPos) {
+       case PORT_ENABLE_STAT:
+               hp->RHportStatus->portStatus &= ~bitPos;
+               break;
+
+       case PORT_RESET_STAT:
+               hp->RHportStatus->portStatus &= ~bitPos;
+               break;
+
+       case PORT_POWER_STAT:
+               hp->RHportStatus->portStatus &= ~bitPos;
+               break;
+
+       case PORT_SUSPEND_STAT:
+               hp->RHportStatus->portStatus &= ~bitPos;
+               break;
+       }
+}
+
+/*****************************************************************
+ *
+ * Function Name: setPortStatus
+ *
+ * This function set the bit position of portStatus.
+ *
+ * Input:  hci = data structure for the host controller
+ *         bitPos = the bit position
+ *
+ * Return value  : none 
+ *                
+ *****************************************************************/
+static void setPortStatus (hci_t * hci, __u16 bitPos)
+{
+       hcipriv_t *hp = &hci->hp;
+       switch (bitPos) {
+       case PORT_ENABLE_STAT:
+               hp->RHportStatus->portStatus |= bitPos;
+               break;
+
+       case PORT_RESET_STAT:
+               hp->RHportStatus->portStatus |= bitPos;
+               break;
+
+       case PORT_POWER_STAT:
+               hp->RHportStatus->portStatus |= bitPos;
+               break;
+
+       case PORT_SUSPEND_STAT:
+               hp->RHportStatus->portStatus |= bitPos;
+               break;
+       }
+}
+
+/*****************************************************************
+ *
+ * Function Name: hc_start
+ *
+ * This function starts the root hub functionality. 
+ *
+ * Input:  hci = data structure for the host controller
+ *
+ * Return value  : 0 
+ *                
+ *****************************************************************/
+static int hc_start (hci_t * hci)
+{
+       DBGFUNC ("Enter hc_start\n");
+
+       rh_connect_rh (hci);
+
+       return 0;
+}
+
+/*****************************************************************
+ *
+ * Function Name: hc_alloc_hci
+ *
+ * This function allocates all data structure and store in the 
+ * private data structure. 
+ *
+ * Input:  hci = data structure for the host controller
+ *
+ * Return value  : 0 
+ *                
+ *****************************************************************/
+static hci_t *__devinit hc_alloc_hci (void)
+{
+       hci_t *hci;
+       hcipriv_t *hp;
+       portstat_t *ps;
+       struct usb_bus *bus;
+
+       DBGFUNC ("Enter hc_alloc_hci\n");
+       hci = (hci_t *) kmalloc (sizeof (hci_t), GFP_KERNEL);
+       if (!hci)
+               return NULL;
+
+       memset (hci, 0, sizeof (hci_t));
+
+       hp = &hci->hp;
+
+       hp->irq = -1;
+       hp->hcport = -1;
+
+       /* setup root hub port status */
+
+       ps = (portstat_t *) kmalloc (sizeof (portstat_t), GFP_KERNEL);
+
+       if (!ps)
+               return NULL;
+       ps->portStatus = PORT_STAT_DEFAULT;
+       ps->portChange = PORT_CHANGE_DEFAULT;
+       hp->RHportStatus = ps;
+
+       hci->nakCnt = 0;
+       hci->last_packet_nak = 0;
+
+       hci->a_td_array.len = 0;
+       hci->i_td_array[0].len = 0;
+       hci->i_td_array[1].len = 0;
+       hci->td_array = &hci->a_td_array;
+       hci->active_urbs = 0;
+       hci->active_trans = 0;
+       INIT_LIST_HEAD (&hci->hci_hcd_list);
+       list_add (&hci->hci_hcd_list, &hci_hcd_list);
+       init_waitqueue_head (&hci->waitq);
+
+       INIT_LIST_HEAD (&hci->ctrl_list);
+       INIT_LIST_HEAD (&hci->bulk_list);
+       INIT_LIST_HEAD (&hci->iso_list);
+       INIT_LIST_HEAD (&hci->intr_list);
+       INIT_LIST_HEAD (&hci->del_list);
+
+       bus = usb_alloc_bus (&hci_device_operations);
+       if (!bus) {
+               kfree (hci);
+               return NULL;
+       }
+
+       hci->bus = bus;
+       bus->hcpriv = (void *) hci;
+
+       return hci;
+}
+
+/*****************************************************************
+ *
+ * Function Name: hc_release_hci
+ *
+ * This function De-allocate all resources  
+ *
+ * Input:  hci = data structure for the host controller
+ *
+ * Return value  : 0 
+ *                
+ *****************************************************************/
+static void hc_release_hci (hci_t * hci)
+{
+       hcipriv_t *hp = &hci->hp;
+
+       DBGFUNC ("Enter hc_release_hci\n");
+
+       /* disconnect all devices */
+       if (hci->bus->root_hub)
+               usb_disconnect (&hci->bus->root_hub);
+
+       hc_reset (hci);
+
+       if (hp->tl)
+               kfree (hp->tl);
+
+       if (hp->hcport > 0) {
+               release_region (hp->hcport, 2);
+               hp->hcport = 0;
+       }
+
+       if (hp->irq >= 0) {
+               free_irq (hp->irq, hci);
+               hp->irq = -1;
+       }
+
+       usb_deregister_bus (hci->bus);
+       usb_free_bus (hci->bus);
+
+       list_del (&hci->hci_hcd_list);
+       INIT_LIST_HEAD (&hci->hci_hcd_list);
+
+       kfree (hci);
+}
+
+/*****************************************************************
+ *
+ * Function Name: init_irq
+ *
+ * This function is board specific.  It sets up the interrupt to 
+ * be an edge trigger and trigger on the rising edge  
+ *
+ * Input: none 
+ *
+ * Return value  : none 
+ *                
+ *****************************************************************/
+void init_irq (void)
+{
+       GPDR &= ~(1 << 13);
+       set_GPIO_IRQ_edge (1 << 13, GPIO_RISING_EDGE);
+}
+
+/*****************************************************************
+ *
+ * Function Name: hc_found_hci
+ *
+ * This function request IO memory regions, request IRQ, and
+ * allocate all other resources. 
+ *
+ * Input: addr = first IO address
+ *        addr2 = second IO address
+ *        irq = interrupt number 
+ *
+ * Return: 0 = success or error condition 
+ *                
+ *****************************************************************/
+static int __devinit hc_found_hci (int addr, int addr2, int irq)
+{
+       hci_t *hci;
+       hcipriv_t *hp;
+
+       DBGFUNC ("Enter hc_found_hci\n");
+       hci = hc_alloc_hci ();
+       if (!hci) {
+               return -ENOMEM;
+       }
+
+       init_irq ();
+       hp = &hci->hp;
+
+       if (!request_region (addr, 256, "SL811 USB HOST")) {
+               DBGERR ("request address %d failed", addr);
+               hc_release_hci (hci);
+               return -EBUSY;
+       }
+       hp->hcport = addr;
+       if (!hp->hcport) {
+               DBGERR ("Error mapping SL811 Memory 0x%x", hp->hcport);
+       }
+
+       if (!request_region (addr2, 256, "SL811 USB HOST")) {
+               DBGERR ("request address %d failed", addr2);
+               hc_release_hci (hci);
+               return -EBUSY;
+       }
+       hp->hcport2 = addr2;
+       if (!hp->hcport2) {
+               DBGERR ("Error mapping SL811 Memory 0x%x", hp->hcport2);
+       }
+
+       if (hc_alloc_trans_buffer (hci)) {
+               hc_release_hci (hci);
+               return -ENOMEM;
+       }
+
+       usb_register_bus (hci->bus);
+
+       if (request_irq (irq, hc_interrupt, 0, "SL811", hci) != 0) {
+               DBGERR ("request interrupt %d failed", irq);
+               hc_release_hci (hci);
+               return -EBUSY;
+       }
+       hp->irq = irq;
+
+       printk (KERN_INFO __FILE__ ": USB SL811 at %x, addr2 = %x, IRQ %d\n",
+               addr, addr2, irq);
+       hc_reset (hci);
+
+       if (hc_start (hci) < 0) {
+               DBGERR ("can't start usb-%x", addr);
+               hc_release_hci (hci);
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+/*****************************************************************
+ *
+ * Function Name: hci_hcd_init
+ *
+ * This is an init function, and it is the first function being called
+ *
+ * Input: none 
+ *
+ * Return: 0 = success or error condition 
+ *                
+ *****************************************************************/
+static int __init hci_hcd_init (void)
+{
+       int ret;
+
+       DBGFUNC ("Enter hci_hcd_init\n");
+       ret = hc_found_hci (base_addr, data_reg_addr, irq);
+
+       return ret;
+}
+
+/*****************************************************************
+ *
+ * Function Name: hci_hcd_cleanup
+ *
+ * This is a cleanup function, and it is called when module is 
+ * unloaded. 
+ *
+ * Input: none 
+ *
+ * Return: none 
+ *                
+ *****************************************************************/
+static void __exit hci_hcd_cleanup (void)
+{
+       struct list_head *hci_l;
+       hci_t *hci;
+
+       DBGFUNC ("Enter hci_hcd_cleanup\n");
+       for (hci_l = hci_hcd_list.next; hci_l != &hci_hcd_list;) {
+               hci = list_entry (hci_l, hci_t, hci_hcd_list);
+               hci_l = hci_l->next;
+               hc_release_hci (hci);
+       }
+}
+
+module_init (hci_hcd_init);
+module_exit (hci_hcd_cleanup);
+
+MODULE_AUTHOR ("Pei Liu <pbl@cypress.com>");
+MODULE_DESCRIPTION ("USB SL811HS Host Controller Driver");
diff --git a/drivers/usb/host/hc_sl811.h b/drivers/usb/host/hc_sl811.h
new file mode 100644 (file)
index 0000000..1618d23
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * SL811HS HCD (Host Controller Driver) for USB.
+ * 
+ * COPYRIGHT (C) by CYPRESS SEMICONDUCTOR INC 
+ * 
+ *
+ */
+
+#define GET_FRAME_NUMBER(hci)  READ_REG32 (hci, HcFmNumber)
+
+/*
+ * Maximum number of root hub ports
+ */
+#define MAX_ROOT_PORTS         15      /* maximum OHCI root hub ports */
+
+/* control and status registers */
+#define HcRevision             0x00
+#define HcControl              0x01
+#define HcCommandStatus                0x02
+#define HcInterruptStatus      0x03
+#define HcInterruptEnable      0x04
+#define HcInterruptDisable     0x05
+#define HcFmInterval           0x0D
+#define HcFmRemaining          0x0E
+#define HcFmNumber             0x0F
+#define HcLSThreshold          0x11
+#define HcRhDescriptorA                0x12
+#define HcRhDescriptorB                0x13
+#define HcRhStatus             0x14
+#define HcRhPortStatus         0x15
+
+#define HcHardwareConfiguration 0x20
+#define HcDMAConfiguration     0x21
+#define HcTransferCounter      0x22
+#define HcuPInterrupt          0x24
+#define HcuPInterruptEnable    0x25
+#define HcChipID               0x27
+#define HcScratch              0x28
+#define HcSoftwareReset                0x29
+#define HcITLBufferLength      0x2A
+#define HcATLBufferLength      0x2B
+#define HcBufferStatus         0x2C
+#define HcReadBackITL0Length   0x2D
+#define HcReadBackITL1Length   0x2E
+#define HcITLBufferPort                0x40
+#define HcATLBufferPort                0x41
+
+/* OHCI CONTROL AND STATUS REGISTER MASKS */
+
+/*
+ * HcControl (control) register masks
+ */
+#define OHCI_CTRL_HCFS         (3 << 6)        /* BUS state mask */
+#define OHCI_CTRL_RWC          (1 << 9)        /* remote wakeup connected */
+#define OHCI_CTRL_RWE          (1 << 10)       /* remote wakeup enable */
+
+/* pre-shifted values for HCFS */
+#define OHCI_USB_RESET         (0 << 6)
+#define OHCI_USB_RESUME                (1 << 6)
+#define OHCI_USB_OPER          (2 << 6)
+#define OHCI_USB_SUSPEND       (3 << 6)
+
+/*
+ * HcCommandStatus (cmdstatus) register masks
+ */
+#define OHCI_HCR       (1 << 0)        /* host controller reset */
+#define OHCI_SO                (3 << 16)       /* scheduling overrun count */
+
+/*
+ * masks used with interrupt registers:
+ * HcInterruptStatus (intrstatus)
+ * HcInterruptEnable (intrenable)
+ * HcInterruptDisable (intrdisable)
+ */
+#define OHCI_INTR_SO   (1 << 0)        /* scheduling overrun */
+
+#define OHCI_INTR_SF   (1 << 2)        /* start frame */
+#define OHCI_INTR_RD   (1 << 3)        /* resume detect */
+#define OHCI_INTR_UE   (1 << 4)        /* unrecoverable error */
+#define OHCI_INTR_FNO  (1 << 5)        /* frame number overflow */
+#define OHCI_INTR_RHSC (1 << 6)        /* root hub status change */
+#define OHCI_INTR_ATD  (1 << 7)        /* scheduling overrun */
+
+#define OHCI_INTR_MIE  (1 << 31)       /* master interrupt enable */
+
+/*
+ * HcHardwareConfiguration
+ */
+#define InterruptPinEnable     (1 << 0)
+#define InterruptPinTrigger    (1 << 1)
+#define InterruptOutputPolarity        (1 << 2)
+#define DataBusWidth16         (1 << 3)
+#define DREQOutputPolarity     (1 << 5)
+#define DACKInputPolarity      (1 << 6)
+#define EOTInputPolarity       (1 << 7)
+#define DACKMode               (1 << 8)
+#define AnalogOCEnable         (1 << 10)
+#define SuspendClkNotStop      (1 << 11)
+#define DownstreamPort15KRSel  (1 << 12)
+
+/* 
+ * HcDMAConfiguration
+ */
+#define DMAReadWriteSelect     (1 << 0)
+#define ITL_ATL_DataSelect     (1 << 1)
+#define DMACounterSelect       (1 << 2)
+#define DMAEnable              (1 << 4)
+#define BurstLen_1             0
+#define BurstLen_4             (1 << 5)
+#define BurstLen_8             (2 << 5)
+
+/*
+ * HcuPInterrupt
+ */
+#define SOFITLInt              (1 << 0)
+#define ATLInt                 (1 << 1)
+#define AllEOTInterrupt                (1 << 2)
+#define OPR_Reg                        (1 << 4)
+#define HCSuspended            (1 << 5)
+#define ClkReady               (1 << 6)
+
+/*
+ * HcBufferStatus
+ */
+#define ITL0BufferFull         (1 << 0)
+#define ITL1BufferFull         (1 << 1)
+#define ATLBufferFull          (1 << 2)
+#define ITL0BufferDone         (1 << 3)
+#define ITL1BufferDone         (1 << 4)
+#define ATLBufferDone          (1 << 5)
+
+/* OHCI ROOT HUB REGISTER MASKS */
+
+/* roothub.portstatus [i] bits */
+#define RH_PS_CCS            0x00000001        /* current connect status */
+#define RH_PS_PES            0x00000002        /* port enable status */
+#define RH_PS_PSS            0x00000004        /* port suspend status */
+#define RH_PS_POCI           0x00000008        /* port over current indicator */
+#define RH_PS_PRS            0x00000010        /* port reset status */
+#define RH_PS_PPS            0x00000100        /* port power status */
+#define RH_PS_LSDA           0x00000200        /* low speed device attached */
+#define RH_PS_CSC            0x00010000        /* connect status change */
+#define RH_PS_PESC           0x00020000        /* port enable status change */
+#define RH_PS_PSSC           0x00040000        /* port suspend status change */
+#define RH_PS_OCIC           0x00080000        /* over current indicator change */
+#define RH_PS_PRSC           0x00100000        /* port reset status change */
+
+/* roothub.status bits */
+#define RH_HS_LPS              0x00000001      /* local power status */
+#define RH_HS_OCI              0x00000002      /* over current indicator */
+#define RH_HS_DRWE             0x00008000      /* device remote wakeup enable */
+#define RH_HS_LPSC             0x00010000      /* local power status change */
+#define RH_HS_OCIC             0x00020000      /* over current indicator change */
+#define RH_HS_CRWE             0x80000000      /* clear remote wakeup enable */
+
+/* roothub.b masks */
+#define RH_B_DR                        0x0000ffff      /* device removable flags */
+#define RH_B_PPCM              0xffff0000      /* port power control mask */
+
+/* roothub.a masks */
+#define        RH_A_NDP                (0xff << 0)     /* number of downstream ports */
+#define        RH_A_PSM                (1 << 8)        /* power switching mode */
+#define        RH_A_NPS                (1 << 9)        /* no power switching */
+#define        RH_A_DT                 (1 << 10)       /* device type (mbz) */
+#define        RH_A_OCPM               (1 << 11)       /* over current protection mode */
+#define        RH_A_NOCP               (1 << 12)       /* no over current protection */
+#define        RH_A_POTPGT             (0xff << 24)    /* power on to power good time */
+
+#define URB_DEL 1
+
+#define PORT_STAT_DEFAULT              0x0100
+#define PORT_CONNECT_STAT              0x1
+#define PORT_ENABLE_STAT               0x2
+#define PORT_SUSPEND_STAT              0x4
+#define PORT_OVER_CURRENT_STAT         0x8
+#define PORT_RESET_STAT                        0x10
+#define PORT_POWER_STAT                        0x100
+#define PORT_LOW_SPEED_DEV_ATTACH_STAT 0x200
+
+#define PORT_CHANGE_DEFAULT            0x0
+#define PORT_CONNECT_CHANGE            0x1
+#define PORT_ENABLE_CHANGE             0x2
+#define PORT_SUSPEND_CHANGE            0x4
+#define PORT_OVER_CURRENT_CHANGE       0x8
+#define PORT_RESET_CHANGE              0x10
+
+/* Port Status Request info */
+
+typedef struct portstat {
+       __u16 portChange;
+       __u16 portStatus;
+} portstat_t;
+
+typedef struct hcipriv {
+       int irq;
+       int disabled;           /* e.g. got a UE, we're hung */
+       atomic_t resume_count;  /* defending against multiple resumes */
+       struct ohci_regs *regs; /* OHCI controller's memory */
+       int hcport;             /* I/O base address */
+       int hcport2;            /* I/O data reg addr */
+
+       struct portstat *RHportStatus;  /* root hub port status */
+
+       int intrstatus;
+       __u32 hc_control;       /* copy of the hc control reg */
+
+       int frame;
+
+       __u8 *tl;
+       int xferPktLen;
+       int atl_len;
+       int atl_buffer_len;
+       int itl0_len;
+       int itl1_len;
+       int itl_buffer_len;
+       int itl_index;
+       int tl_last;
+       int units_left;
+
+} hcipriv_t;
+struct hci;
+
+#define cClt        0          // Control
+#define cISO        1          // ISO
+#define cBULK       2          // BULK
+#define cInt        3          // Interrupt
+#define ISO_BIT     0x10
+
+/*-------------------------------------------------------------------------
+ * EP0 use for configuration and Vendor Specific command interface
+ *------------------------------------------------------------------------*/
+#define cMemStart       0x10
+#define EP0Buf          0x40   /* SL11H/SL811H memory start at 0x40 */
+#define EP0Len          0x40   /* Length of config buffer EP0Buf */
+#define EP1Buf          0x60
+#define EP1Len          0x40
+
+/*-------------------------------------------------------------------------
+ * SL11H/SL811H memory from 80h-ffh use as ping-pong buffer.
+ *------------------------------------------------------------------------*/
+#define uBufA           0x80   /* buffer A address for DATA0 */
+#define uBufB           0xc0   /* buffer B address for DATA1 */
+#define uXferLen        0x40   /* xfer length */
+#define sMemSize        0xc0   /* Total SL11 memory size */
+#define cMemEnd         256
+
+/*-------------------------------------------------------------------------
+ * SL811H Register Control memory map
+ * --Note: 
+ *      --SL11H only has one control register set from 0x00-0x04
+ *      --SL811H has two control register set from 0x00-0x04 and 0x08-0x0c
+ *------------------------------------------------------------------------*/
+
+#define EP0Control      0x00
+#define EP0Address      0x01
+#define EP0XferLen      0x02
+#define EP0Status       0x03
+#define EP0Counter      0x04
+
+#define EP1Control      0x08
+#define EP1Address      0x09
+#define EP1XferLen      0x0a
+#define EP1Status       0x0b
+#define EP1Counter      0x0c
+
+#define CtrlReg         0x05
+#define IntEna          0x06
+                        // 0x07 is reserved
+#define IntStatus       0x0d
+#define cDATASet        0x0e
+#define cSOFcnt         0x0f
+#define IntMask         0x57   /* Reset|DMA|EP0|EP2|EP1 for IntEna */
+#define HostMask        0x47   /* Host request command  for IntStatus */
+#define ReadMask        0xd7   /* Read mask interrupt   for IntStatus */
+
+/*-------------------------------------------------------------------------
+ * Standard Chapter 9 definition
+ *-------------------------------------------------------------------------
+ */
+#define GET_STATUS      0x00
+#define CLEAR_FEATURE   0x01
+#define SET_FEATURE     0x03
+#define SET_ADDRESS     0x05
+#define GET_DESCRIPTOR  0x06
+#define SET_DESCRIPTOR  0x07
+#define GET_CONFIG      0x08
+#define SET_CONFIG      0x09
+#define GET_INTERFACE   0x0a
+#define SET_INTERFACE   0x0b
+#define SYNCH_FRAME     0x0c
+
+#define DEVICE          0x01
+#define CONFIGURATION   0x02
+#define STRING          0x03
+#define INTERFACE       0x04
+#define ENDPOINT        0x05
+
+/*-------------------------------------------------------------------------
+ * SL11H/SL811H definition
+ *-------------------------------------------------------------------------
+ */
+#define DATA0_WR       0x07    // (Arm+Enable+tranmist to Host+DATA0)
+#define DATA1_WR       0x47    // (Arm+Enable+tranmist to Host on DATA1)
+#define ZDATA0_WR      0x05    // (Arm+Transaction Ignored+tranmist to Host+DATA0)
+#define ZDATA1_WR      0x45    // (Arm+Transaction Ignored+tranmist to Host+DATA1)
+#define DATA0_RD       0x03    // (Arm+Enable+received from Host+DATA0)
+#define DATA1_RD       0x43    // (Arm+Enable+received from Host+DATA1)
+
+#define PID_SETUP      0x2d    // USB Specification 1.1 Standard Definition
+#define PID_SOF                0xA5
+#define PID_IN         0x69
+#define PID_OUT                0xe1
+
+#define MAX_RETRY      0xffff
+#define TIMEOUT                5               /* 2 mseconds */
+
+#define SL11H_HOSTCTLREG       0
+#define SL11H_BUFADDRREG       1
+#define SL11H_BUFLNTHREG       2
+#define SL11H_PKTSTATREG       3       /* read */
+#define SL11H_PIDEPREG         3       /* write */
+#define SL11H_XFERCNTREG       4       /* read */
+#define SL11H_DEVADDRREG       4       /* write */
+#define SL11H_CTLREG1          5
+#define SL11H_INTENBLREG       6
+
+#define SL11H_HOSTCTLREG_B     8
+#define SL11H_BUFADDRREG_B     9
+#define SL11H_BUFLNTHREG_B     0x0A
+#define SL11H_PKTSTATREG_B     0x0B    /* read */
+#define SL11H_PIDEPREG_B       0x0B    /* write */
+#define SL11H_XFERCNTREG_B     0x0C    /* read */
+#define SL11H_DEVADDRREG_B     0x0C    /* write */
+
+#define SL11H_INTSTATREG       0x0D    /* write clears bitwise */
+#define SL11H_HWREVREG         0x0E    /* read */
+#define SL11H_SOFLOWREG                0x0E    /* write */
+#define SL11H_SOFTMRREG                0x0F    /* read */
+#define SL11H_CTLREG2          0x0F    /* write */
+#define SL11H_DATA_START       0x10
+
+/* Host control register bits (addr 0) */
+#define SL11H_HCTLMASK_ARM     1
+#define SL11H_HCTLMASK_ENBLEP  2
+#define SL11H_HCTLMASK_WRITE   4
+#define SL11H_HCTLMASK_ISOCH   0x10
+#define SL11H_HCTLMASK_AFTERSOF        0x20
+#define SL11H_HCTLMASK_SEQ     0x40
+#define SL11H_HCTLMASK_PREAMBLE        0x80
+
+/* Packet status register bits (addr 3) */
+#define SL11H_STATMASK_ACK     1
+#define SL11H_STATMASK_ERROR   2
+#define SL11H_STATMASK_TMOUT   4
+#define SL11H_STATMASK_SEQ     8
+#define SL11H_STATMASK_SETUP   0x10
+#define SL11H_STATMASK_OVF     0x20
+#define SL11H_STATMASK_NAK     0x40
+#define SL11H_STATMASK_STALL   0x80
+
+/* Control register 1 bits (addr 5) */
+#define SL11H_CTL1MASK_DSBLSOF 1
+#define SL11H_CTL1MASK_NOTXEOF2        4
+#define SL11H_CTL1MASK_DSTATE  0x18
+#define SL11H_CTL1MASK_NSPD    0x20
+#define SL11H_CTL1MASK_SUSPEND 0x40
+#define SL11H_CTL1MASK_CLK12   0x80
+
+#define SL11H_CTL1VAL_RESET    8
+
+/* Interrut enable (addr 6) and interrupt status register bits (addr 0xD) */
+#define SL11H_INTMASK_XFERDONE 1
+#define SL11H_INTMASK_SOFINTR  0x10
+#define SL11H_INTMASK_INSRMV   0x20
+#define SL11H_INTMASK_USBRESET 0x40
+#define SL11H_INTMASK_DSTATE   0x80    /* only in status reg */
+
+/* HW rev and SOF lo register bits (addr 0xE) */
+#define SL11H_HWRMASK_HWREV    0xF0
+
+/* SOF counter and control reg 2 (addr 0xF) */
+#define SL11H_CTL2MASK_SOFHI   0x3F
+#define SL11H_CTL2MASK_DSWAP   0x40
+#define SL11H_CTL2MASK_HOSTMODE        0xae
+
diff --git a/drivers/usb/host/hc_sl811_rh.c b/drivers/usb/host/hc_sl811_rh.c
new file mode 100644 (file)
index 0000000..25be13c
--- /dev/null
@@ -0,0 +1,526 @@
+
+/*-------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------*
+ * SL811HS virtual root hub
+ *  
+ * based on usb-ohci.c by R. Weissgaerber et al.
+ *-------------------------------------------------------------------------*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+#undef DEBUG
+#endif
+static __u32 getPortStatusAndChange (hci_t * hci);
+static void setPortStatus (hci_t * hci, __u16 bitPos);
+static void setPortChange (hci_t * hci, __u16 bitPos);
+static void clrPortStatus (hci_t * hci, __u16 bitPos);
+static void clrPortChange (hci_t * hci, __u16 bitPos);
+static int USBReset (hci_t * hci);
+static int cc_to_error (int cc);
+
+/*-------------------------------------------------------------------------*
+ * Virtual Root Hub 
+ *-------------------------------------------------------------------------*/
+
+/* Device descriptor */
+static __u8 root_hub_dev_des[] = {
+       0x12,                   /*  __u8  bLength; */
+       0x01,                   /*  __u8  bDescriptorType; Device */
+       0x10,                   /*  __u16 bcdUSB; v1.1 */
+       0x01,
+       0x09,                   /*  __u8  bDeviceClass; HUB_CLASSCODE */
+       0x00,                   /*  __u8  bDeviceSubClass; */
+       0x00,                   /*  __u8  bDeviceProtocol; */
+       0x08,                   /*  __u8  bMaxPacketSize0; 8 Bytes */
+       0x00,                   /*  __u16 idVendor; */
+       0x00,
+       0x00,                   /*  __u16 idProduct; */
+       0x00,
+       0x00,                   /*  __u16 bcdDevice; */
+       0x00,
+       0x00,                   /*  __u8  iManufacturer; */
+       0x02,                   /*  __u8  iProduct; */
+       0x01,                   /*  __u8  iSerialNumber; */
+       0x01                    /*  __u8  bNumConfigurations; */
+};
+
+/* Configuration descriptor */
+static __u8 root_hub_config_des[] = {
+       0x09,                   /*  __u8  bLength; */
+       0x02,                   /*  __u8  bDescriptorType; Configuration */
+       0x19,                   /*  __u16 wTotalLength; */
+       0x00,
+       0x01,                   /*  __u8  bNumInterfaces; */
+       0x01,                   /*  __u8  bConfigurationValue; */
+       0x00,                   /*  __u8  iConfiguration; */
+       0x40,                   /*  __u8  bmAttributes; 
+                                  Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 
+                                  4..0: resvd */
+       0x00,                   /*  __u8  MaxPower; */
+
+       /* interface */
+       0x09,                   /*  __u8  if_bLength; */
+       0x04,                   /*  __u8  if_bDescriptorType; Interface */
+       0x00,                   /*  __u8  if_bInterfaceNumber; */
+       0x00,                   /*  __u8  if_bAlternateSetting; */
+       0x01,                   /*  __u8  if_bNumEndpoints; */
+       0x09,                   /*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
+       0x00,                   /*  __u8  if_bInterfaceSubClass; */
+       0x00,                   /*  __u8  if_bInterfaceProtocol; */
+       0x00,                   /*  __u8  if_iInterface; */
+
+       /* endpoint */
+       0x07,                   /*  __u8  ep_bLength; */
+       0x05,                   /*  __u8  ep_bDescriptorType; Endpoint */
+       0x81,                   /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
+       0x03,                   /*  __u8  ep_bmAttributes; Interrupt */
+       0x02,                   /*  __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */
+       0x00,
+       0xff                    /*  __u8  ep_bInterval; 255 ms */
+};
+
+/* Hub class-specific descriptor is constructed dynamically */
+
+/***************************************************************************
+ * Function Name : rh_send_irq
+ * 
+ * This function examine the port change in the virtual root hub.
+ * 
+ * Note: This function assumes only one port exist in the root hub.
+ *
+ * Input:  hci = data structure for the host controller
+ *         rh_data = The pointer to port change data
+ *         rh_len = length of the data in bytes
+ *
+ * Return: length of data  
+ **************************************************************************/
+static int rh_send_irq (hci_t * hci, void *rh_data, int rh_len)
+{
+       int num_ports;
+       int i;
+       int ret;
+       int len;
+       __u8 data[8];
+
+       DBGFUNC ("enter rh_send_irq: \n");
+
+       /* Assuming the root hub has one port.  This value need to change if
+        * there are more than one port for the root hub
+        */
+
+       num_ports = 1;
+
+       /* The root hub status is not implemented, it basically has two fields:
+        *     -- Local Power Status
+        *     -- Over Current Indicator
+        *     -- Local Power Change
+        *     -- Over Current Indicator
+        *
+        * Right now, It is assume the power is good and no changes 
+        */
+
+       *(__u8 *) data = 0;
+
+       ret = *(__u8 *) data;
+
+       /* Has the port status change within the root hub: It checks for
+        *      -- Port Connect Status change
+        *      -- Port Enable Change
+        */
+
+       for (i = 0; i < num_ports; i++) {
+               *(__u8 *) (data + (i + 1) / 8) |=
+                   (((getPortStatusAndChange (hci) >> 16) & (PORT_CONNECT_STAT | PORT_ENABLE_STAT)) ? 1 : 0) << ((i + 1) % 8);
+               ret += *(__u8 *) (data + (i + 1) / 8);
+
+               /* After the port change is read, it should be reset so the next time 
+                * is it doesn't trigger a change again */
+
+       }
+       len = i / 8 + 1;
+
+       if (ret > 0) {
+               memcpy (rh_data, data, min (len, min (rh_len, (int)sizeof (data))));
+               return len;
+       }
+       return 0;
+}
+
+/***************************************************************************
+ * Function Name : rh_int_timer_do
+ * 
+ * This function is called when the timer expires.  It gets the the port 
+ * change data and pass along to the upper protocol.
+ * 
+ * Note:  The virtual root hub interrupt pipe are polled by the timer
+ *        every "interval" ms
+ *
+ * Input:  ptr = ptr to the urb
+ *
+ * Return: none  
+ **************************************************************************/
+static void rh_int_timer_do (unsigned long ptr)
+{
+       int len;
+       struct urb *urb = (struct urb *) ptr;
+       hci_t *hci = urb->dev->bus->hcpriv;
+
+       DBGFUNC ("enter rh_int_timer_do\n");
+
+       if (hci->rh.send) {
+               len = rh_send_irq (hci, urb->transfer_buffer,
+                                  urb->transfer_buffer_length);
+               if (len > 0) {
+                       urb->actual_length = len;
+                       if (urb_debug == 2)
+                               urb_print (urb, "RET-t(rh)",
+                                          usb_pipeout (urb->pipe));
+
+                       if (urb->complete) {
+                               urb->complete (urb);
+                       }
+               }
+       }
+
+       /* re-activate the timer */
+       rh_init_int_timer (urb);
+}
+
+/***************************************************************************
+ * Function Name : rh_init_int_timer
+ * 
+ * This function creates a timer that act as interrupt pipe in the
+ * virtual hub.   
+ * 
+ * Note:  The virtual root hub's interrupt pipe are polled by the timer
+ *        every "interval" ms
+ *
+ * Input: urb = USB request block 
+ *
+ * Return: 0  
+ **************************************************************************/
+static int rh_init_int_timer (struct urb * urb)
+{
+       hci_t *hci = urb->dev->bus->hcpriv;
+       hci->rh.interval = urb->interval;
+
+       init_timer (&hci->rh.rh_int_timer);
+       hci->rh.rh_int_timer.function = rh_int_timer_do;
+       hci->rh.rh_int_timer.data = (unsigned long) urb;
+       hci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000;
+       add_timer (&hci->rh.rh_int_timer);
+
+       return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* helper macro */
+#define OK(x)                  len = (x); break
+
+/***************************************************************************
+ * Function Name : rh_submit_urb
+ * 
+ * This function handles all USB request to the the virtual root hub
+ * 
+ * Input: urb = USB request block 
+ *
+ * Return: 0  
+ **************************************************************************/
+static int rh_submit_urb (struct urb * urb)
+{
+       struct usb_device *usb_dev = urb->dev;
+       hci_t *hci = usb_dev->bus->hcpriv;
+       unsigned int pipe = urb->pipe;
+       struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet;
+       void *data = urb->transfer_buffer;
+       int leni = urb->transfer_buffer_length;
+       int len = 0;
+       int status = TD_CC_NOERROR;
+       __u32 datab[4];
+       __u8 *data_buf = (__u8 *) datab;
+
+       __u16 bmRType_bReq;
+       __u16 wValue;
+       __u16 wIndex;
+       __u16 wLength;
+
+       DBGFUNC ("enter rh_submit_urb\n");
+       if (usb_pipeint (pipe)) {
+               hci->rh.urb = urb;
+               hci->rh.send = 1;
+               hci->rh.interval = urb->interval;
+               rh_init_int_timer (urb);
+               urb->status = cc_to_error (TD_CC_NOERROR);
+
+               return 0;
+       }
+
+       bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8);
+       wValue = le16_to_cpu (cmd->wValue);
+       wIndex = le16_to_cpu (cmd->wIndex);
+       wLength = le16_to_cpu (cmd->wLength);
+
+       DBG ("rh_submit_urb, req = %d(%x) len=%d",
+            bmRType_bReq, bmRType_bReq, wLength);
+
+       switch (bmRType_bReq) {
+               /* Request Destination:
+                  without flags: Device, 
+                  RH_INTERFACE: interface, 
+                  RH_ENDPOINT: endpoint,
+                  RH_CLASS means HUB here, 
+                  RH_OTHER | RH_CLASS  almost ever means HUB_PORT here 
+                */
+
+       case RH_GET_STATUS:
+               *(__u16 *) data_buf = cpu_to_le16 (1);
+               OK (2);
+
+       case RH_GET_STATUS | RH_INTERFACE:
+               *(__u16 *) data_buf = cpu_to_le16 (0);
+               OK (2);
+
+       case RH_GET_STATUS | RH_ENDPOINT:
+               *(__u16 *) data_buf = cpu_to_le16 (0);
+               OK (2);
+
+       case RH_GET_STATUS | RH_CLASS:
+               *(__u32 *) data_buf = cpu_to_le32 (0);
+               OK (4);
+
+       case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+               *(__u32 *) data_buf =
+                   cpu_to_le32 (getPortStatusAndChange (hci));
+               OK (4);
+
+       case RH_CLEAR_FEATURE | RH_ENDPOINT:
+               switch (wValue) {
+               case (RH_ENDPOINT_STALL):
+                       OK (0);
+               }
+               break;
+
+       case RH_CLEAR_FEATURE | RH_CLASS:
+               switch (wValue) {
+               case RH_C_HUB_LOCAL_POWER:
+                       OK (0);
+
+               case (RH_C_HUB_OVER_CURRENT):
+                       /* Over Current Not Implemented */
+                       OK (0);
+               }
+               break;
+
+       case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+               switch (wValue) {
+               case (RH_PORT_ENABLE):
+                       clrPortStatus (hci, PORT_ENABLE_STAT);
+                       OK (0);
+
+               case (RH_PORT_SUSPEND):
+                       clrPortStatus (hci, PORT_SUSPEND_STAT);
+                       OK (0);
+
+               case (RH_PORT_POWER):
+                       clrPortStatus (hci, PORT_POWER_STAT);
+                       OK (0);
+
+               case (RH_C_PORT_CONNECTION):
+                       clrPortChange (hci, PORT_CONNECT_STAT);
+                       OK (0);
+
+               case (RH_C_PORT_ENABLE):
+                       clrPortChange (hci, PORT_ENABLE_STAT);
+                       OK (0);
+
+               case (RH_C_PORT_SUSPEND):
+                       clrPortChange (hci, PORT_SUSPEND_STAT);
+                       OK (0);
+
+               case (RH_C_PORT_OVER_CURRENT):
+                       clrPortChange (hci, PORT_OVER_CURRENT_STAT);
+                       OK (0);
+
+               case (RH_C_PORT_RESET):
+                       clrPortChange (hci, PORT_RESET_STAT);
+                       OK (0);
+               }
+               break;
+
+       case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+               switch (wValue) {
+               case (RH_PORT_SUSPEND):
+                       setPortStatus (hci, PORT_SUSPEND_STAT);
+                       OK (0);
+
+               case (RH_PORT_RESET):
+                       setPortStatus (hci, PORT_RESET_STAT);
+                       // USBReset(hci);
+                       clrPortChange (hci,
+                                      PORT_CONNECT_CHANGE | PORT_ENABLE_CHANGE
+                                      | PORT_SUSPEND_CHANGE |
+                                      PORT_OVER_CURRENT_CHANGE);
+                       setPortChange (hci, PORT_RESET_CHANGE);
+                       clrPortStatus (hci, PORT_RESET_STAT);
+                       setPortStatus (hci, PORT_ENABLE_STAT);
+
+                       OK (0);
+
+               case (RH_PORT_POWER):
+                       setPortStatus (hci, PORT_POWER_STAT);
+                       OK (0);
+
+               case (RH_PORT_ENABLE):
+                       setPortStatus (hci, PORT_ENABLE_STAT);
+                       OK (0);
+               }
+               break;
+
+       case RH_SET_ADDRESS:
+               hci->rh.devnum = wValue;
+               OK (0);
+
+       case RH_GET_DESCRIPTOR:
+               DBGVERBOSE ("rh_submit_urb: RH_GET_DESCRIPTOR, wValue = 0x%x\n", wValue);
+               switch ((wValue & 0xff00) >> 8) {
+               case (0x01):    /* device descriptor */
+                       len = min (leni, min ((__u16)sizeof (root_hub_dev_des), wLength));
+                       data_buf = root_hub_dev_des;
+                       OK (len);
+
+               case (0x02):    /* configuration descriptor */
+                       len = min (leni, min ((__u16)sizeof (root_hub_config_des), wLength));
+                       data_buf = root_hub_config_des;
+                       OK (len);
+
+               case (0x03):    /* string descriptors */
+                       len = usb_root_hub_string (wValue & 0xff, (int) (long) 0,
+                                                  "SL811HS", data, wLength);
+                       if (len > 0) {
+                               data_buf = data;
+                               OK (min (leni, len));
+                       }
+
+               default:
+                       status = SL11H_STATMASK_STALL;
+               }
+               break;
+
+       case RH_GET_DESCRIPTOR | RH_CLASS:
+               data_buf[0] = 9;        // min length;
+               data_buf[1] = 0x29;
+               data_buf[2] = 1;        // # of downstream port
+               data_buf[3] = 0;
+               datab[1] = 0;
+               data_buf[5] = 50;       // 100 ms for port reset
+               data_buf[7] = 0xfc;     // which port is attachable
+               if (data_buf[2] < 7) {
+                       data_buf[8] = 0xff;
+               } else {
+               }
+
+               len = min (leni, min ((__u16)data_buf[0], wLength));
+               OK (len);
+
+       case RH_GET_CONFIGURATION:
+               *(__u8 *) data_buf = 0x01;
+               OK (1);
+
+       case RH_SET_CONFIGURATION:
+               OK (0);
+
+       default:
+               DBGERR ("unsupported root hub command");
+               status = SL11H_STATMASK_STALL;
+       }
+
+       len = min (len, leni);
+       if (data != data_buf)
+               memcpy (data, data_buf, len);
+       urb->actual_length = len;
+       urb->status = cc_to_error (status);
+
+       urb->hcpriv = NULL;
+       urb->dev = NULL;
+       if (urb->complete) {
+               urb->complete (urb);
+       }
+
+       return 0;
+}
+
+/***************************************************************************
+ * Function Name : rh_unlink_urb
+ * 
+ * This function unlinks the URB 
+ * 
+ * Input: urb = USB request block 
+ *
+ * Return: 0  
+ **************************************************************************/
+static int rh_unlink_urb (struct urb * urb)
+{
+       hci_t *hci = urb->dev->bus->hcpriv;
+
+       DBGFUNC ("enter rh_unlink_urb\n");
+       if (hci->rh.urb == urb) {
+               hci->rh.send = 0;
+               del_timer (&hci->rh.rh_int_timer);
+               hci->rh.urb = NULL;
+
+               urb->hcpriv = NULL;
+               usb_put_dev (urb->dev);
+               urb->dev = NULL;
+               if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+                       urb->status = -ECONNRESET;
+                       if (urb->complete) {
+                               urb->complete (urb);
+                       }
+               } else
+                       urb->status = -ENOENT;
+       }
+       return 0;
+}
+
+/***************************************************************************
+ * Function Name : rh_connect_rh
+ * 
+ * This function connect the virtual root hub to the USB stack 
+ * 
+ * Input: urb = USB request block 
+ *
+ * Return: 0  
+ **************************************************************************/
+static int rh_connect_rh (hci_t * hci)
+{
+       struct usb_device *usb_dev;
+
+       hci->rh.devnum = 0;
+       usb_dev = usb_alloc_dev (NULL, hci->bus);
+       if (!usb_dev)
+               return -ENOMEM;
+
+       hci->bus->root_hub = usb_dev;
+       usb_connect (usb_dev);
+       if (usb_new_device (usb_dev) != 0) {
+               usb_free_dev (usb_dev);
+               return -ENODEV;
+       }
+
+       return 0;
+}