Axis Area
Public Area
Special pages
NOTE
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