ETRAX 100LX USB driver patch

This patch applies to the ETRAX 100LX USB (CRIS V10) driver in the Linux 2.6 kernel supplied in the 2.10 software distribution (os-linux-2.6-tag–devboard-R2_10-4).

Index: os/linux-2.6/drivers/usb/host/hc-crisv10.c
===================================================================
RCS file: /usr/local/cvs/linux/os/linux-2.6/drivers/usb/host/hc-crisv10.c,v
retrieving revision 1.10
diff -b -u -p -r1.10 hc-crisv10.c
--- os/linux-2.6/drivers/usb/host/hc-crisv10.c	26 Feb 2007 19:58:29 -0000	1.10
+++ os/linux-2.6/drivers/usb/host/hc-crisv10.c	18 Sep 2007 06:43:26 -0000
@@ -1311,11 +1311,208 @@ static int rh_control_request(struct usb
 
 int rh_set_port_feature(__u8 bPort, __u16 wFeature) {
   __u8 bUsbCommand = 0;
+  __u8 reset_cnt;
   switch(wFeature) {
   case USB_PORT_FEAT_RESET:
     rh_dbg("SetPortFeature: reset\n");
+
+    if (rh.wPortStatusPrev[bPort] &
+        IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes))
+    {
+      __u8 restart_controller = 0;
+
+      if ( (rh.wPortStatusPrev[0] &
+            IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes)) &&
+           (rh.wPortStatusPrev[1] &
+            IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes)) )
+      {
+        /* Both ports is enabled. The USB controller will not change state. */
+        restart_controller = 0;
+      }
+      else
+      {
+        /* Only ports is enabled. The USB controller will change state and
+           must be restarted. */
+        restart_controller = 1;
+      }
+      /*
+        In ETRAX 100LX it's not possible to reset an enabled root hub port.
+        The workaround is to disable and enable the port before resetting it.
+        Disabling the port can, if both ports are disabled at once, cause the
+        USB controller to change state to HOST_MODE state. 
+        The USB controller state transition causes a lot of unwanted
+        interrupts that must be avoided.
+        Disabling the USB controller status and port status interrupts before
+        disabling/resetting the port stops these interrupts.
+
+        These actions are performed:
+        1. Disable USB controller status and port status interrupts.
+        2. Disable the port
+        3. Wait for the port to be disabled.
+        4. Enable the port.
+        5. Wait for the port to be enabled.
+        6. Reset the port.
+        7. Wait for for the reset to end.
+        8. Wait for the USB controller entering started state.
+        9. Order the USB controller to running state.
+        10. Wait for the USB controller reaching running state.
+        11. Clear all interrupts generated during the disable/enable/reset
+            procedure.
+        12. Enable the USB controller status and port status interrupts.
+      */
+
+      /* 1. Disable USB controller status and USB port status interrupts. */
+      *R_USB_IRQ_MASK_CLR = IO_STATE(R_USB_IRQ_MASK_CLR, ctl_status, clr);
+      __asm__ __volatile__ ("  nop");
+      *R_USB_IRQ_MASK_CLR = IO_STATE(R_USB_IRQ_MASK_CLR, port_status, clr);
+      __asm__ __volatile__ ("  nop");
+      
+      {
+
+        /* Since an root hub port reset shall be 50 ms and the ETRAX 100LX
+           root hub port reset is 10 ms we must perform 5 port resets to
+           achieve a proper root hub port reset. */
+        for (reset_cnt = 0; reset_cnt < 5; reset_cnt ++)
+        {
+          rh_dbg("Disable Port %d\n", bPort + 1);
+
+          /* 2. Disable the port*/
+          if (bPort == 0)
+          {
+            *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, yes);
+          }
+          else
+          {
+            *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, yes);
+          }
+
+          /* 3. Wait for the port to be disabled. */
+          while ( (bPort == 0) ?
+                  *R_USB_RH_PORT_STATUS_1 &
+                    IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes) :
+                  *R_USB_RH_PORT_STATUS_2 &
+                    IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes) ) {}
+
+          rh_dbg("Port %d is disabled. Enable it!\n", bPort + 1);
+
+          /* 4. Enable the port. */
+          if (bPort == 0)
+          {
+            *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no);
+          }
+          else
+          {
+            *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no);
+          }
+
+          /* 5. Wait for the port to be enabled again.  */
+          while (!( (bPort == 0) ?
+                    *R_USB_RH_PORT_STATUS_1 &
+                      IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes) :
+                    *R_USB_RH_PORT_STATUS_2 &
+                      IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes) ) ) {}
+
+          rh_dbg("Port %d is enabled.\n", bPort + 1);
+
+          /* 6. Reset the port */
+          crisv10_ready_wait();
+          *R_USB_COMMAND =
+            ( (bPort == 0) ?
+              IO_STATE(R_USB_COMMAND, port_sel, port1):
+              IO_STATE(R_USB_COMMAND, port_sel, port2) ) |
+            IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+            IO_STATE(R_USB_COMMAND, busy,     no) |
+            IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+          rh_dbg("Port %d is resetting.\n", bPort + 1);
+
+          /* 7. The USB specification says that we should wait for at least
+             10ms for device recover */
+          udelay(10500); /* 10,5ms blocking wait */
+    
+          crisv10_ready_wait();
+        }
+      }
+
+
+      /* Check if the USB controller needs to be restarted. */
+      if (restart_controller)
+      {
+        /* 8. Wait for the USB controller entering started state. */
+        while (!(*R_USB_STATUS & IO_STATE(R_USB_STATUS, started, yes))) {}
+
+        /* 9. Order the USB controller to running state. */
+        crisv10_ready_wait();
+        *R_USB_COMMAND =
+          IO_STATE(R_USB_COMMAND, port_sel, nop) |
+          IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+          IO_STATE(R_USB_COMMAND, busy,     no) |
+          IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
+
+        /* 10. Wait for the USB controller reaching running state. */
+        while (!(*R_USB_STATUS & IO_STATE(R_USB_STATUS, running, yes))) {}
+      }
+
+      /* 11. Clear any controller or port satus interrupts before enabling
+             the interrupts. */
+      {
+        u16 dummy;
+
+        /* Clear the port status interrupt of the reset port. */
+        if (bPort == 0)
+        {
+          rh_dbg("Clearing port 1 interrupts\n");
+          dummy = *R_USB_RH_PORT_STATUS_1;
+        }
+        else
+        {
+          rh_dbg("Clearing port 2 interrupts\n");
+          dummy = *R_USB_RH_PORT_STATUS_2;
+        }
+
+        if (restart_controller)
+        {
+          /* The USB controller is restarted. Clear all interupts. */
+          rh_dbg("Clearing all interrupts\n");
+          dummy = *R_USB_STATUS;
+          dummy = *R_USB_RH_PORT_STATUS_1;
+          dummy = *R_USB_RH_PORT_STATUS_2;
+        }
+      }
+
+      /* 12. Enable USB controller status and USB port status interrupts.  */
+      *R_USB_IRQ_MASK_SET = IO_STATE(R_USB_IRQ_MASK_SET, ctl_status, set);
+      __asm__ __volatile__ ("  nop");
+      *R_USB_IRQ_MASK_SET = IO_STATE(R_USB_IRQ_MASK_SET, port_status, set);
+      __asm__ __volatile__ ("  nop");
+
+    }
+    else
+    {
+
     bUsbCommand |= IO_STATE(R_USB_COMMAND, port_cmd, reset);
-    goto set;
+      /* Select which port via the port_sel field */
+      bUsbCommand |= IO_FIELD(R_USB_COMMAND, port_sel, bPort+1);
+
+      /* Make sure the controller isn't busy. */
+      crisv10_ready_wait();
+      /* Send out the actual command to the USB controller */
+      *R_USB_COMMAND = bUsbCommand;
+
+      /* Wait a while for controller to first become started after port reset */
+      udelay(12000); /* 12ms blocking wait */
+      
+      /* Make sure the controller isn't busy. */
+      crisv10_ready_wait();
+
+      /* If all enabled ports were disabled the host controller goes down into
+         started mode, so we need to bring it back into the running state.
+         (This is safe even if it's already in the running state.) */
+      *R_USB_COMMAND =
+        IO_STATE(R_USB_COMMAND, port_sel, nop) |
+        IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+        IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
+    }
+
     break;
   case USB_PORT_FEAT_SUSPEND:
     rh_dbg("SetPortFeature: suspend\n");
@@ -1343,24 +1540,6 @@ int rh_set_port_feature(__u8 bPort, __u1
     crisv10_ready_wait();
     /* Send out the actual command to the USB controller */
     *R_USB_COMMAND = bUsbCommand;
-
-    /* If port reset then also bring USB controller into running state */
-    if(wFeature == USB_PORT_FEAT_RESET) {
-      /* Wait a while for controller to first become started after port reset */
-      udelay(12000); /* 12ms blocking wait */
-      
-      /* Make sure the controller isn't busy. */
-      crisv10_ready_wait();
-
-      /* If all enabled ports were disabled the host controller goes down into
-	 started mode, so we need to bring it back into the running state.
-	 (This is safe even if it's already in the running state.) */
-      *R_USB_COMMAND =
-	IO_STATE(R_USB_COMMAND, port_sel, nop) |
-	IO_STATE(R_USB_COMMAND, port_cmd, reset) |
-	IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
-    }
-
     break;
   default:
     rh_dbg("SetPortFeature: unknown feature\n");
@@ -1694,9 +1873,8 @@ static void restart_dma8_sub0(void) {
     while (DUMMY_EPID == IO_EXTRACT(USB_EP_command, epid, ep->command)) {
       ep = (struct USB_EP_Desc *)phys_to_virt(ep->next);
     }
-    /* Advance the DMA to the next EP descriptor that is not a DUMMY_EPID.
-     * ep->next is already a physical address; no need for a virt_to_phys. */
-    *R_DMA_CH8_SUB0_EP = ep->next;
+    /* Advance the DMA to the next EP descriptor that is not a DUMMY_EPID. */
+    *R_DMA_CH8_SUB0_EP = virt_to_phys(ep);
     /* Restart the DMA */
     *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
   }
@@ -2610,7 +2788,7 @@ static int tc_setup_epid(struct usb_host
     isoc_epid_counter++;
     if(isoc_epid_counter == 1) {
       isoc_warn("Enabled Isoc eof interrupt\n");
-      *R_USB_IRQ_MASK_SET |= IO_STATE(R_USB_IRQ_MASK_SET, iso_eof, set);
+      *R_USB_IRQ_MASK_SET = IO_STATE(R_USB_IRQ_MASK_SET, iso_eof, set);
     }
   }
 
@@ -2639,7 +2817,7 @@ static void tc_free_epid(struct usb_host
     ASSERT(isoc_epid_counter > 0);
     isoc_epid_counter--;
     if(isoc_epid_counter == 0) {
-      *R_USB_IRQ_MASK_SET &= ~IO_STATE(R_USB_IRQ_MASK_SET, iso_eof, set);
+      *R_USB_IRQ_MASK_CLR = IO_STATE(R_USB_IRQ_MASK_CLR, iso_eof, clr);
       isoc_warn("Disabled Isoc eof interrupt\n");
     }
   }

Save the patch in a file, e.g. usb_patch. Apply the patch from the root of your tree:

patch -p 0 < usb_patch

rebuild the kernel:

make -C packages/os/linux-2.6 install

and build a new image:

make images

Jesper Bengtsson 2007/09/18 08:01

 
2_10_v10_usb_dr_patch.txt · Last modified: 2011/11/24 08:31 by admin
 
All text is available under the terms of the GNU Free Documentation License (see Copyrights for details).