/*******************************************************************************

This software file (the "File") is distributed by Marvell International Ltd. 
or its affiliate(s) under the terms of the GNU General Public License Version 2, 
June 1991 (the "License").  You may use, redistribute and/or modify this File 
in accordance with the terms and conditions of the License, a copy of which 
is available along with the File in the license.txt file or by writing to the 
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 
or on the worldwide web at http://www.gnu.org/licenses/gpl.txt.

THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED 
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY 
DISCLAIMED.  The GPL License provides additional details about this warranty 
disclaimer.

(C) Copyright 2004 - 2007 Marvell Semiconductor Israel Ltd. All Rights Reserved.
(C) Copyright 1999 - 2004 Chipidea Microelectronica, S.A. All Rights Reserved.

*******************************************************************************/

#include "mvUsbDevApi.h"
#include "mvUsbDevPrv.h"


/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_init
*  Returned Value : USB_OK or error code
*  Comments       :
*        Initializes the USB device controller.
*
*END*-----------------------------------------------------------------*/
uint_8 _usb_dci_vusb20_init
   (
      /* [IN] the USB device controller to initialize */
      uint_8                     devnum,

      /* [OUT] the USB_dev_initialize state structure */
      _usb_device_handle         handle
   )
{ /* Body */
    USB_DEV_STATE_STRUCT_PTR    usb_dev_ptr;
    uint_32                     temp;
    uint_8*                     pBuf;
    unsigned long               phyAddr;
      
    usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;

    usb_dev_ptr->CAP_REGS_PTR = 
       (VUSB20_REG_STRUCT_PTR)USB_get_cap_reg_addr(devnum);
      
    /* Get the base address of the VUSB_HS registers */
    usb_dev_ptr->DEV_PTR = 
      (VUSB20_REG_STRUCT_PTR)(((uint_32)usb_dev_ptr->CAP_REGS_PTR) + 
       (USB_32BIT_LE(usb_dev_ptr->CAP_REGS_PTR->REGISTERS.CAPABILITY_REGISTERS.CAPLENGTH_HCIVER) & 
                                                                EHCI_CAP_LEN_MASK));

    /* Get the maximum number of endpoints supported by this USB controller */
    usb_dev_ptr->MAX_ENDPOINTS = 
      (USB_32BIT_LE(usb_dev_ptr->CAP_REGS_PTR->REGISTERS.CAPABILITY_REGISTERS.DCC_PARAMS) & 
                                                VUSB20_DCC_MAX_ENDPTS_SUPPORTED);

    USB_printf("USB init: CAP_REGS=0x%x, DEV_REGS=0x%x, MAX_EP=%d\n",
                (unsigned)usb_dev_ptr->CAP_REGS_PTR, (unsigned)usb_dev_ptr->DEV_PTR, 
                usb_dev_ptr->MAX_ENDPOINTS);

    temp = (usb_dev_ptr->MAX_ENDPOINTS * 2);
   
    pBuf = (uint_8*)USB_uncached_memalloc(temp*sizeof(VUSB20_EP_QUEUE_HEAD_STRUCT), 
                                          2048, &phyAddr);      
    if (pBuf == NULL) 
    {
        USB_printf("_usb_dci_vusb20_init, malloc of %d bytes in Uncached area failed\n",
                        temp*sizeof(VUSB20_EP_QUEUE_HEAD_STRUCT));
        return USBERR_ALLOC;
    }

    /****************************************************************   
      Assign QH base
    ****************************************************************/   
    usb_dev_ptr->EP_QUEUE_HEAD_BASE = pBuf;
    usb_dev_ptr->EP_QUEUE_HEAD_PHYS = (uint_32)phyAddr;

    /* Align the endpoint queue head to 2K boundary */   
    usb_dev_ptr->EP_QUEUE_HEAD_PTR = (VUSB20_EP_QUEUE_HEAD_STRUCT_PTR)
                USB_MEM2048_ALIGN((uint_32)usb_dev_ptr->EP_QUEUE_HEAD_BASE);

    usb_dev_ptr->EP_QUEUE_HEAD_SIZE = temp*sizeof(VUSB20_EP_QUEUE_HEAD_STRUCT) +
                                      ((uint_32)usb_dev_ptr->EP_QUEUE_HEAD_PTR - 
                                       (uint_32)usb_dev_ptr->EP_QUEUE_HEAD_BASE);

    /****************************************************************   
      Zero out the memory allocated
    ****************************************************************/   
    USB_memzero( (void*)usb_dev_ptr->EP_QUEUE_HEAD_PTR, 
                 temp*sizeof(VUSB20_EP_QUEUE_HEAD_STRUCT));

    USB_printf("USB EP_QH: Base=%p (0x%x), Aligned(%d)=%p, Size=%d\n",
                usb_dev_ptr->EP_QUEUE_HEAD_BASE, usb_dev_ptr->EP_QUEUE_HEAD_PHYS,
                2048, usb_dev_ptr->EP_QUEUE_HEAD_PTR, usb_dev_ptr->EP_QUEUE_HEAD_SIZE);

    /****************************************************************   
      Assign DTD base
    ****************************************************************/   
    pBuf = (uint_8*)USB_uncached_memalloc(MAX_EP_TR_DESCRS*sizeof(VUSB20_EP_TR_STRUCT), 
                                          32, &phyAddr);      
    if (pBuf == NULL) 
    {
        USB_printf("_usb_dci_vusb20_init, malloc of %d bytes in Uncached area failed\n",
                        MAX_EP_TR_DESCRS*sizeof(VUSB20_EP_TR_STRUCT));
        return USBERR_ALLOC;
    }

    usb_dev_ptr->DTD_BASE_PTR = pBuf;
    usb_dev_ptr->DTD_BASE_PHYS = (uint_32)phyAddr;
    
    /* Align the dTD base to 32 byte boundary */   
    usb_dev_ptr->DTD_ALIGNED_BASE_PTR = (VUSB20_EP_TR_STRUCT_PTR)
                        USB_MEM32_ALIGN((uint_32)usb_dev_ptr->DTD_BASE_PTR);

    usb_dev_ptr->DTD_SIZE = MAX_EP_TR_DESCRS*sizeof(VUSB20_EP_TR_STRUCT) +
                                ((uint_32)usb_dev_ptr->EP_QUEUE_HEAD_PTR - 
                                 (uint_32)usb_dev_ptr->EP_QUEUE_HEAD_BASE);

    /****************************************************************   
      Zero out the memory allocated
    ****************************************************************/   
    USB_memzero((void*)usb_dev_ptr->DTD_ALIGNED_BASE_PTR, 
                MAX_EP_TR_DESCRS*sizeof(VUSB20_EP_TR_STRUCT));

    /****************************************************************   
      Assign SCRATCH Structure base
    ****************************************************************/   
    /* Allocate memory for internal scratch structure */   
    pBuf = USB_memalloc(MAX_EP_TR_DESCRS*sizeof(SCRATCH_STRUCT));
    if (pBuf == NULL) 
    {
        USB_printf("_usb_dci_vusb20_init, malloc of %d bytes failed\n",
                        MAX_EP_TR_DESCRS*sizeof(SCRATCH_STRUCT));
        return USBERR_ALLOC;
    }
    usb_dev_ptr->SCRATCH_STRUCT_BASE = (SCRATCH_STRUCT_PTR)pBuf;
    USB_memzero(usb_dev_ptr->SCRATCH_STRUCT_BASE, 
            MAX_EP_TR_DESCRS*sizeof(SCRATCH_STRUCT));

    USB_printf("USB dTD(%d): Base=%p (0x%x), Aligned(%d)=%p, Size=%d, Scratch=%p\n",
                MAX_EP_TR_DESCRS, usb_dev_ptr->DTD_BASE_PTR, usb_dev_ptr->DTD_BASE_PHYS,
                32, usb_dev_ptr->DTD_ALIGNED_BASE_PTR, usb_dev_ptr->DTD_SIZE,
                usb_dev_ptr->SCRATCH_STRUCT_BASE);

#ifdef USB_UNDERRUN_WA
    usbSramBase = (uint_8*)USB_get_sram_addr(&usbSramSize);
    if (usbSramBase == NULL) 
    {
        USB_printf("_usb_dci_vusb20_init, SRAM is not available\n");                        
        return USBERR_ALLOC;
    }
    USB_memzero(usbSramBase, usbSramSize);
    USB_printf("USB WA_Queue: base=%p, size=%d, parts=%d\n", 
                    usbSramBase, usbSramSize, global_wa_sram_parts);
#endif /* USB_UNDERRUN_WA */

    usb_dev_ptr->USB_STATE = ARC_USB_STATE_UNKNOWN;

    /* Initialize the VUSB_HS controller */   
    _usb_dci_vusb20_chip_initialize((pointer)usb_dev_ptr);

    return USB_OK;   
} /* EndBody */

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_chip_initialize
*  Returned Value : USB_OK or error code
*  Comments       :
*        Initializes the USB device controller.
*
*END*-----------------------------------------------------------------*/
void _usb_dci_vusb20_chip_initialize
   (
      /* [IN] the USB_dev_initialize state structure */
      _usb_device_handle         handle
   )
{ /* Body */
    USB_DEV_STATE_STRUCT_PTR         usb_dev_ptr;
    VUSB20_REG_STRUCT_PTR            dev_ptr;
    VUSB20_EP_QUEUE_HEAD_STRUCT_PTR  ep_queue_head_ptr;
    VUSB20_EP_TR_STRUCT_PTR          dTD_ptr;
    uint_32                          i, port_control;
    SCRATCH_STRUCT_PTR               temp_scratch_ptr;
    volatile unsigned long           delay;

    ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_INIT, "chip_initialize\n");
   
    usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;

    dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR;
   
    /* Stop the controller */
    dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD &= ~(USB_32BIT_LE(EHCI_CMD_RUN_STOP));
      
    /* Reset the controller to get default values */
    dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD = USB_32BIT_LE(EHCI_CMD_CTRL_RESET);

    USB_printf("USB Init: Wait for RESET completed\n");
   
    delay = 0x100000;
    while (dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD & 
                                    (USB_32BIT_LE(EHCI_CMD_CTRL_RESET))) 
    {
        /* Wait for the controller reset to complete */
        delay--;
        if(delay == 0)
            break;
    } /* EndWhile */

    if(delay == 0)
    {
        USB_printf("USB Init: Wait for RESET completed TIMEOUT\n");
    }
    else
    {
        USB_printf("USB Init: RESET completed\n");
    }
    /* Call BSP callback to complete reset process */
    USB_reset_complete(usb_dev_ptr->DEV_NUM);

    /* Initialize the internal dTD head and tail to NULL */   
    usb_dev_ptr->DTD_HEAD = NULL;
    usb_dev_ptr->DTD_TAIL = NULL;
    usb_dev_ptr->DTD_ENTRIES = 0; 
    usb_dev_ptr->ERROR_STATE = 0;

   /* Make sure the 16 MSBs of this register are 0s */
   dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPT_SETUP_STAT = USB_32BIT_LE(0);
   
   ep_queue_head_ptr = usb_dev_ptr->EP_QUEUE_HEAD_PTR;

   /* Initialize all device queue heads */
   for (i=0; i<(usb_dev_ptr->MAX_ENDPOINTS*2); i++) 
   {
      /* Interrupt on Setup packet */
      (ep_queue_head_ptr + i)->MAX_PKT_LENGTH = (USB_32BIT_LE(
          ((uint_32)USB_MAX_CTRL_PAYLOAD << VUSB_EP_QUEUE_HEAD_MAX_PKT_LEN_POS) | 
            VUSB_EP_QUEUE_HEAD_IOS));

      (ep_queue_head_ptr + i)->NEXT_DTD_PTR = (USB_32BIT_LE(VUSB_EP_QUEUE_HEAD_NEXT_TERMINATE));
   } /* Endfor */

   /* Configure the Endpoint List Address */
   dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.EP_LIST_ADDR = 
                            USB_32BIT_LE(USB_EP_QH_VIRT_TO_PHYS(usb_dev_ptr, ep_queue_head_ptr));
      
   port_control = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]);
   if (usb_dev_ptr->CAP_REGS_PTR->REGISTERS.CAPABILITY_REGISTERS.HCS_PARAMS & 
                                        USB_32BIT_LE(VUSB20_HCS_PARAMS_PORT_POWER_CONTROL_FLAG)) 
   {
      port_control &= (~EHCI_PORTSCX_W1C_BITS | ~EHCI_PORTSCX_PORT_POWER);
   } /* Endif */
   
   if(usb_dev_ptr->FORCE_FS == TRUE)
   {
       port_control |= EHCI_PORTSCX_FORCE_FULL_SPEED_CONNECT;
   }
   else
   {
       port_control &= (~EHCI_PORTSCX_FORCE_FULL_SPEED_CONNECT);
   }
   dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0] = USB_32BIT_LE(port_control);

   dTD_ptr = usb_dev_ptr->DTD_ALIGNED_BASE_PTR;

   temp_scratch_ptr = usb_dev_ptr->SCRATCH_STRUCT_BASE;

   /* Enqueue all the dTDs */   
   for (i=0; i<MAX_EP_TR_DESCRS; i++) 
   {
      dTD_ptr->SCRATCH_PTR = temp_scratch_ptr;
      dTD_ptr->SCRATCH_PTR->FREE = _usb_dci_vusb20_free_dTD;
      /* Set the dTD to be invalid */
      dTD_ptr->NEXT_TR_ELEM_PTR = USB_32BIT_LE(VUSBHS_TD_NEXT_TERMINATE);
      /* Set the Reserved fields to 0 */
      dTD_ptr->SIZE_IOC_STS &= ~(USB_32BIT_LE(VUSBHS_TD_RESERVED_FIELDS));
      dTD_ptr->SCRATCH_PTR->PRIVATE = (pointer)usb_dev_ptr;
      _usb_dci_vusb20_free_dTD((pointer)dTD_ptr);
      dTD_ptr++;
      temp_scratch_ptr++;
   } /* Endfor */
} /* EndBody */

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_free_dTD
*  Returned Value : void
*  Comments       :
*        Enqueues a dTD onto the free DTD ring.
*
*END*-----------------------------------------------------------------*/

void _usb_dci_vusb20_free_dTD
   (
      /* [IN] the dTD to enqueue */
      pointer  dTD_ptr
   )
{ /* Body */
   USB_DEV_STATE_STRUCT_PTR     usb_dev_ptr;
   int                          lockKey;

   usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)(((VUSB20_EP_TR_STRUCT_PTR)dTD_ptr)->SCRATCH_PTR->PRIVATE);

   ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_TRACE, "free_dTD: dTD_ptr=0x%x\n", (unsigned)dTD_ptr);

   ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.free_dTD_count++));


   /*
   ** This function can be called from any context, and it needs mutual
   ** exclusion with itself.
   */
   lockKey = USB_lock();

   /*
   ** Add the dTD to the free dTD queue (linked via PRIVATE) and
   ** increment the tail to the next descriptor
   */
   EHCI_DTD_QADD(usb_dev_ptr->DTD_HEAD, usb_dev_ptr->DTD_TAIL, (VUSB20_EP_TR_STRUCT_PTR)dTD_ptr);
   usb_dev_ptr->DTD_ENTRIES++;

   USB_unlock(lockKey);

} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_add_dTD
*  Returned Value : USB_OK or error code
*  Comments       :
*        Adds a device transfer desriptor(s) to the queue.
*
*END*-----------------------------------------------------------------*/
uint_8 _usb_dci_vusb20_add_dTD
   (
      /* [IN] the USB_dev_initialize state structure */
      _usb_device_handle         handle,
     
      /* [IN] The transfer descriptor address */
      XD_STRUCT_PTR              xd_ptr
   )
{ /* Body */
   USB_DEV_STATE_STRUCT_PTR         usb_dev_ptr;
   VUSB20_REG_STRUCT_PTR            dev_ptr;
   VUSB20_EP_TR_STRUCT_PTR          dTD_ptr, temp_dTD_ptr, first_dTD_ptr = NULL;
   VUSB20_EP_QUEUE_HEAD_STRUCT_PTR  ep_queue_head_ptr;
   uint_32                          curr_pkt_len, remaining_len; 
   uint_32                          curr_offset, temp, bit_pos;
   volatile unsigned long           timeout;
   
   /*********************************************************************
   For a optimal implementation, we need to detect the fact that
   we are adding DTD to an empty list. If list is empty, we can
   actually skip several programming steps esp. those for ensuring
   that there is no race condition.The following boolean will be useful
   in skipping some code here.
   *********************************************************************/
   boolean           list_empty = FALSE;
   
   usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;
   dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR;

   remaining_len = xd_ptr->WTOTALLENGTH;
   
   curr_offset = 0;
   temp = (2*xd_ptr->EP_NUM + xd_ptr->BDIRECTION);
   bit_pos = (1 << (16 * xd_ptr->BDIRECTION + xd_ptr->EP_NUM));
   
   ep_queue_head_ptr = (VUSB20_EP_QUEUE_HEAD_STRUCT_PTR)usb_dev_ptr->EP_QUEUE_HEAD_PTR + temp;

   /*********************************************************************
   This loops iterates through the length of the transfer and divides
   the data in to DTDs each handling the a max of 0x4000 bytes of data.
   The first DTD in the list is stored in a pointer called first_dTD_ptr.
   This pointer is later linked in to QH for processing by the hardware.   
   *********************************************************************/

    do 
    {
        /* Check if we need to split the transfer into multiple dTDs */
        if (remaining_len > VUSB_EP_MAX_LENGTH_TRANSFER) 
        {
            curr_pkt_len = VUSB_EP_MAX_LENGTH_TRANSFER;
        } 
        else 
        {
            curr_pkt_len = remaining_len;
        } /* Endif */
   
        /* Get a dTD from the queue */   
        EHCI_DTD_QGET(usb_dev_ptr->DTD_HEAD, usb_dev_ptr->DTD_TAIL, dTD_ptr);
   
        if (!dTD_ptr) 
        {
            USB_printf("Error: Can't get dTD\n");
            return USBERR_TR_FAILED;
        } /* Endif */

        ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_add_count++));

        remaining_len -= curr_pkt_len;

        usb_dev_ptr->DTD_ENTRIES--;

        if (curr_offset == 0) 
        {
            first_dTD_ptr = dTD_ptr;
        } /* Endif */

        /* Zero the dTD. Leave the last 4 bytes as that is the scratch pointer */
        USB_memzero((void *) dTD_ptr,(sizeof(VUSB20_EP_TR_STRUCT) - 4));

        /* Initialize the dTD */
        dTD_ptr->SCRATCH_PTR->PRIVATE = handle;
   
        /* Set the Terminate bit */
        dTD_ptr->NEXT_TR_ELEM_PTR = USB_32BIT_LE(VUSB_EP_QUEUE_HEAD_NEXT_TERMINATE);

        /*************************************************************
        FIX ME: For hig-speed and high-bandwidth ISO IN endpoints,
        we must initialize the multiplied field so that Host can issues
        multiple IN transactions on the endpoint. See the DTD data
        structure for MultiIO field.
      
        S Garg 11/06/2003
        *************************************************************/
      
        /* Fill in the transfer size */
        if (!remaining_len) 
        {
            dTD_ptr->SIZE_IOC_STS = USB_32BIT_LE((curr_pkt_len << 
                    VUSBHS_TD_LENGTH_BIT_POS) | (VUSBHS_TD_IOC) | (VUSBHS_TD_STATUS_ACTIVE));
        } 
        else 
        {
            dTD_ptr->SIZE_IOC_STS = USB_32BIT_LE((curr_pkt_len << VUSBHS_TD_LENGTH_BIT_POS) 
                                                   | VUSBHS_TD_STATUS_ACTIVE);
        } /* Endif */
   
        /* Set the reserved field to 0 */
        dTD_ptr->SIZE_IOC_STS &= ~USB_32BIT_LE(VUSBHS_TD_RESERVED_FIELDS);

        /* 4K apart buffer page pointers */
        if(xd_ptr->WSTARTADDRESS != NULL)
        {
            uint_32 physAddr = USB_virt_to_phys((uint_8*)xd_ptr->WSTARTADDRESS + curr_offset);

            dTD_ptr->BUFF_PTR0 = USB_32BIT_LE(physAddr);
            
            physAddr += 4096;
            dTD_ptr->BUFF_PTR1 = USB_32BIT_LE(physAddr);

            physAddr += 4096;
            dTD_ptr->BUFF_PTR2 = USB_32BIT_LE(physAddr);

            physAddr += 4096;
            dTD_ptr->BUFF_PTR3 = USB_32BIT_LE(physAddr);

            physAddr += 4096;
            dTD_ptr->BUFF_PTR4 = USB_32BIT_LE(physAddr);
        }
        else
        {
            dTD_ptr->BUFF_PTR0 = dTD_ptr->BUFF_PTR1 = dTD_ptr->BUFF_PTR2 = 0;
            dTD_ptr->BUFF_PTR3 = dTD_ptr->BUFF_PTR4 = 0;
        }
        curr_offset += curr_pkt_len;

      /* Maintain the first and last device transfer descriptor per 
      ** endpoint and direction 
      */
      if (!usb_dev_ptr->EP_DTD_HEADS[temp]) 
      {
         usb_dev_ptr->EP_DTD_HEADS[temp] = dTD_ptr;
         /***********************************************
         If list does not have a head, it means that list
         is empty. An empty condition is detected.
         ***********************************************/
         list_empty = TRUE;
      } /* Endif */ 
   
      /* Check if the transfer is to be queued at the end or beginning */
      temp_dTD_ptr = usb_dev_ptr->EP_DTD_TAILS[temp];
      
      /* Remember which XD to use for this dTD */
      dTD_ptr->SCRATCH_PTR->XD_FOR_THIS_DTD = (pointer)xd_ptr;
      
      /* New tail */
      usb_dev_ptr->EP_DTD_TAILS[temp] = dTD_ptr;
      if (temp_dTD_ptr) 
      {
         /* Should not do |=. The Terminate bit should be zero */
         temp_dTD_ptr->NEXT_TR_ELEM_PTR = USB_32BIT_LE(USB_DTD_VIRT_TO_PHYS(usb_dev_ptr, dTD_ptr));
      } /* Endif */
   } while (remaining_len); /* EndWhile */


   /**************************************************************
   In the loop above DTD has already been added to the list
   However endpoint has not been primed yet. If list is not empty 
   we need safter ways to add DTD to the existing list. 
   Else we just skip to adding DTD to QH safely.
   **************************************************************/
   
    if(list_empty == FALSE)
    {
        volatile boolean    read_safe = FALSE;                      
        uint_32             prime, temp_ep_stat=0;

        /*********************************************************
        Hardware v3.2+ require the use of semaphore to ensure that
        QH is safely updated.
        *********************************************************/
        ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_add_not_empty_count++));

        /*********************************************************
        Check the prime bit. If set goto done
        *********************************************************/
        prime = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME);
/*
        USB_printf("%03d: Add not empty: bit_pos=%x, prime=0x%x, status=0x%x\n",
                    usb_dev_ptr->STATS.usb_add_not_empty_count, prime, bit_pos,
                    USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS),
                    USB_32BIT_LE(ep_queue_head_ptr->SIZE_IOC_INT_STS) );
*/
        if(prime & bit_pos)
        {
            timeout = 0x1000;
            while( dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME
                    & USB_32BIT_LE(bit_pos) ) 
            {
               /* Wait for the ENDPTPRIME to go to zero */
                timeout--;
                if(timeout <= 0)
                {
                    USB_printf(
                        "timeout: CTRL=%x, PRIME=%x, STAT=%x, INTR=%x, ADDR=%x, PORTSC=%x, dTD=%p, temp_dTD=%p\n",
                        USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[0]),
                        USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME), 
                        USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS), 
                        USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_STS),
                        USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.DEVICE_ADDR),
                        USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]), 
                        first_dTD_ptr, temp_dTD_ptr);

                    _usb_ep_status(handle, xd_ptr->EP_NUM, xd_ptr->BDIRECTION);

                    return USBERR_TR_FAILED;
                }
            } /* EndWhile */

            /*ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_TRANSFER,*/
            if(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS & USB_32BIT_LE(bit_pos))
            {
                goto done;
            }
        }

        read_safe = FALSE;
        timeout = 1000000;
        while(read_safe == FALSE)
        {
            timeout--;
            if(timeout <= 0)
            {
                USB_printf("%s: Timeout for ATDTW_TRIPWIRE reg = 0x%x\n", __FUNCTION__, 
                    (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD));
                return USBERR_TR_FAILED;
            }

           /*********************************************************
           start with setting the semaphores
           *********************************************************/
           dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD |= 
                                          USB_32BIT_LE(EHCI_CMD_ATDTW_TRIPWIRE_SET);
               
           /*********************************************************
           Read the endpoint status
           *********************************************************/
           temp_ep_stat = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS) 
                                        & bit_pos; 

           /*********************************************************
           Reread the ATDTW semaphore bit to check if it is cleared.
           When hardware see a hazard, it will clear the bit or
           else we remain set to 1 and we can proceed with priming
           of endpoint if not already primed.
           *********************************************************/
           if( dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD & 
                                          USB_32BIT_LE(EHCI_CMD_ATDTW_TRIPWIRE_SET))
           {
               read_safe = TRUE;
           }

        }/*end while loop */

        /*********************************************************
        Clear the semaphore
        *********************************************************/
        dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD &= 
                                       USB_32BIT_LE(EHCI_CMD_ATDTW_TRIPWIRE_CLEAR);

        /*********************************************************
         * If endpoint is not active, we activate it now.               
         *********************************************************/
         if(!temp_ep_stat)
         {
            /* No other transfers on the queue */
            ep_queue_head_ptr->NEXT_DTD_PTR = USB_32BIT_LE(
                        USB_DTD_VIRT_TO_PHYS(usb_dev_ptr, first_dTD_ptr));
            ep_queue_head_ptr->SIZE_IOC_INT_STS = 0;

            /* Prime the Endpoint */
            dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME = USB_32BIT_LE(bit_pos);
         }
    }
    else
    {
         /* No other transfers on the queue */
         ep_queue_head_ptr->NEXT_DTD_PTR = USB_32BIT_LE(
                            USB_DTD_VIRT_TO_PHYS(usb_dev_ptr, first_dTD_ptr));
         ep_queue_head_ptr->SIZE_IOC_INT_STS = 0;
   
         /* Prime the Endpoint */
         dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME = USB_32BIT_LE(bit_pos);
         /* delay */
         timeout = 0x100;
         while(timeout > 0)
             timeout--;

         dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME = USB_32BIT_LE(bit_pos);
    }

done:
   if(first_dTD_ptr == NULL)
       USB_printf("ERROR !!!! first_dTD_ptr=NULL\n");

   ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_TRANSFER,
               " add_%d: fri=0x%x, ep=%d%s, buf=%p, size=%d, xd=%p, dTD=%p %p, empty=%d\n",
               usb_dev_ptr->STATS.usb_add_count & 0xFFFF, 
               USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_FRINDEX), 
               xd_ptr->EP_NUM, xd_ptr->BDIRECTION ? "in" : "out", 
               xd_ptr->WSTARTADDRESS, (int)xd_ptr->WTOTALLENGTH, 
               xd_ptr, (unsigned)first_dTD_ptr, 
               usb_dev_ptr->EP_DTD_HEADS[temp], list_empty);


   return USB_OK;
   /* End CR 1015 */
} /* EndBody */


/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_process_tr_complete
*  Returned Value : None
*  Comments       :
*        Services transaction complete interrupt
*
*END*-----------------------------------------------------------------*/
void _usb_dci_vusb20_process_tr_complete
   (
      /* [IN] the USB_dev_initialize state structure */
      _usb_device_handle         handle
   )
{ /* Body */
   USB_DEV_STATE_STRUCT_PTR                     usb_dev_ptr;
   volatile VUSB20_REG_STRUCT_PTR               dev_ptr;
   volatile VUSB20_EP_TR_STRUCT_PTR             dTD_ptr; 
   VUSB20_EP_TR_STRUCT_PTR                      temp_dTD_ptr;
   VUSB20_EP_QUEUE_HEAD_STRUCT_PTR              ep_queue_head_ptr;
   uint_32                                      temp, i, ep_num = 0, direction = 0, bit_pos;
   uint_32                                      remaining_length = 0;
   uint_32                                      actual_transfer_length = 0;
   uint_32                                      counter, errors = 0;
   XD_STRUCT_PTR                                xd_ptr;
   XD_STRUCT_PTR                                temp_xd_ptr = NULL;
   uint_8_ptr                                   buff_start_address = NULL;
   boolean                                      endpoint_detected = FALSE;
   
   usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;
   dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR;

   ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_complete_isr_count++));

   /* We use separate loops for ENDPTSETUPSTAT and ENDPTCOMPLETE because the 
   ** setup packets are to be read ASAP 
   */
   
   /* Process all Setup packet received interrupts */
   bit_pos = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPT_SETUP_STAT);
   
   if (bit_pos) 
   {
      ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_SETUP, "setup_isr: bit_pos=0x%x\n", (unsigned)bit_pos);
      for(i=0; i<USB_MAX_CONTROL_ENDPOINTS; i++) 
      {
         if (bit_pos & (1 << i)) 
         {
            ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_setup_count++));
            _usb_device_call_service(handle, i, TRUE, 0, 0, 8, 0);
         } /* Endif */
      } /* Endfor */
   } /* Endif */
   
   /* Don't clear the endpoint setup status register here. It is cleared as a 
   ** setup packet is read out of the buffer 
   */

   /* Process non-setup transaction complete interrupts */   
   bit_pos = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCOMPLETE);

   /* Clear the bits in the register */
   dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCOMPLETE = USB_32BIT_LE(bit_pos);
   
   if (bit_pos) 
   {
        ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_complete_count++));

        /* Get the endpoint number and the direction of transfer */
        counter = 0;
        for (i=0; i<(ARC_USB_MAX_ENDPOINTS*2); i++) 
        {
            endpoint_detected = FALSE;
            if ((i < ARC_USB_MAX_ENDPOINTS) && (bit_pos & (1 << i)))
            {
                ep_num = i;
                direction = ARC_USB_RECV;
                endpoint_detected = TRUE;
            }
            else
            {
                if( (i >= ARC_USB_MAX_ENDPOINTS) && 
                    (bit_pos & (1 << (i+16-ARC_USB_MAX_ENDPOINTS))))
                {
                    ep_num = (i - ARC_USB_MAX_ENDPOINTS);
                    direction = ARC_USB_SEND;
                    endpoint_detected = TRUE;
                }            
            }

            if(endpoint_detected)
            {
                temp = (2*ep_num + direction);

                /* Get the first dTD */      
                dTD_ptr = usb_dev_ptr->EP_DTD_HEADS[temp];
            
                ep_queue_head_ptr = (VUSB20_EP_QUEUE_HEAD_STRUCT_PTR)usb_dev_ptr->EP_QUEUE_HEAD_PTR + temp;

                /* Process all the dTDs for respective transfers */
                while (dTD_ptr) 
                {            
                    if (USB_32BIT_LE(dTD_ptr->SIZE_IOC_STS) & VUSBHS_TD_STATUS_ACTIVE) 
                    {
                        /* No more dTDs to process. Next one is owned by VUSB */
                        if(counter == 0)
                        {
                            ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ISR, "tr_complete - break: ep=%d %s, bit_pos=0x%x\n",
                                    (unsigned)ep_num, direction ? "SEND" : "RECV", (unsigned)bit_pos);

                            ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_empty_complete_count++));
                        }
                        break;
                    } /* Endif */
               
                    /* Get the correct internal transfer descriptor */
                    xd_ptr = (XD_STRUCT_PTR)dTD_ptr->SCRATCH_PTR->XD_FOR_THIS_DTD;
                    if (xd_ptr) 
                    {
                        buff_start_address = xd_ptr->WSTARTADDRESS;
                        actual_transfer_length = xd_ptr->WTOTALLENGTH;
                        temp_xd_ptr = xd_ptr;
                    } /* Endif */
               
                    /* Get the address of the next dTD */
                    temp_dTD_ptr = (VUSB20_EP_TR_STRUCT_PTR)USB_DTD_PHYS_TO_VIRT(usb_dev_ptr, 
                                (uint_32)(USB_32BIT_LE(dTD_ptr->NEXT_TR_ELEM_PTR) & VUSBHS_TD_ADDR_MASK) );
                  
                    /* Read the errors */
                    errors = (USB_32BIT_LE(dTD_ptr->SIZE_IOC_STS) & VUSBHS_TD_ERROR_MASK);                  
                    if (!errors) 
                    {
                        /* No errors */
                        /* Get the length of transfer from the current dTD */   
                        remaining_length += ((USB_32BIT_LE(dTD_ptr->SIZE_IOC_STS) & VUSB_EP_TR_PACKET_SIZE) >> 16);
                        actual_transfer_length -= remaining_length;
                    } 
                    else 
                    {
                        if (errors & VUSBHS_TD_STATUS_HALTED) 
                        {
                            /* Clear the errors and Halt condition */
                            ep_queue_head_ptr->SIZE_IOC_INT_STS &= USB_32BIT_LE(~errors);
                        } /* Endif */

                        ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ERROR, 
                            "complete_tr ERROR: ep=%d %s: STS=0x%x, dTD=%p, dTD_next=%p, xd=%p, qh->sts=0x%x\n", 
                                    (unsigned)ep_num, direction ? "SEND" : "RECV", 
                                    (unsigned)dTD_ptr->SIZE_IOC_STS, dTD_ptr, temp_dTD_ptr, 
                                    xd_ptr, ep_queue_head_ptr->SIZE_IOC_INT_STS);
                    } /* Endif */
               
                    /* Retire the processed dTD */
                    counter++;
                    _usb_dci_vusb20_cancel_transfer(handle, ep_num, direction);
                    if( (temp_dTD_ptr == NULL) || 
                        (temp_dTD_ptr->SCRATCH_PTR->XD_FOR_THIS_DTD != temp_xd_ptr) ) 
                    {
                        /* Transfer complete. Call the register service function for the endpoint */
                        ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_complete_ep_count[temp]++));

#if defined(USB_UNDERRUN_WA)
                        if( (direction == ARC_USB_SEND) && 
                            (((ep_queue_head_ptr->MAX_PKT_LENGTH >> 16) & 0x7FF) > global_wa_threshold) )
                            usbSendComplete(handle, ep_num, FALSE, direction, 
                                   buff_start_address, actual_transfer_length, errors);
                        else
#endif /* USB_UNDERRUN_WA */
                            _usb_device_call_service(handle, ep_num, FALSE, direction, 
                                   buff_start_address, actual_transfer_length, errors);
                        remaining_length = 0;

                        ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_TRANSFER,
                            "comp_%d: fri=0x%x, ep=%d%s, buf=%p, size=%d, xd=%p, dTD=%p %p %p, COMP=0x%x\n", 
                            usb_dev_ptr->STATS.usb_complete_count & 0xFFFF, 
                            USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_FRINDEX), 
                            (unsigned)ep_num, direction ? "in" : "out", 
                            buff_start_address, actual_transfer_length,
                            temp_xd_ptr, dTD_ptr, temp_dTD_ptr, usb_dev_ptr->EP_DTD_HEADS[temp], (unsigned)bit_pos);

                    } /* Endif */
                    else
                    {
                        ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_TRANSFER, "tr_complete not completed: ep=%d %s\n", 
                                (unsigned)ep_num, direction ? "SEND" : "RECV");
                    }
                    if( (temp_dTD_ptr == NULL) && (usb_dev_ptr->EP_DTD_HEADS[temp] != NULL) )
                    {
/*
                        USB_printf("tr_complete: ep=%d, temp_dTD=%p, dTD_ptr=%p (%p), DTD_HEADS=%p, remain=%d\n",
                                    temp, temp_dTD_ptr, dTD_ptr, 
                                    USB_DTD_PHYS_TO_VIRT(usb_dev_ptr, (uint_32)(USB_32BIT_LE(dTD_ptr->NEXT_TR_ELEM_PTR) & VUSBHS_TD_ADDR_MASK) ),
                                    usb_dev_ptr->EP_DTD_HEADS[temp], remaining_length);
*/
                        dTD_ptr = usb_dev_ptr->EP_DTD_HEADS[temp];
                    }
                    else
                    {
                        dTD_ptr = temp_dTD_ptr;
                    }
                    errors = 0;
                } /* Endwhile */
            } /* Endif */
        } /* Endfor */
        ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, 
               ( {if(usb_dev_ptr->STATS.usb_complete_max_count < counter) 
                            usb_dev_ptr->STATS.usb_complete_max_count = counter;}));
   } /* Endif */
} /* EndBody */

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_isr
*  Returned Value : None
*  Comments       :
*        Services all the VUSB_HS interrupt sources
*
*END*-----------------------------------------------------------------*/
void _usb_dci_vusb20_isr
   (
      _usb_device_handle handle
   )
{ /* Body */
   USB_DEV_STATE_STRUCT_PTR     usb_dev_ptr;
   VUSB20_REG_STRUCT_PTR        dev_ptr;
   uint_32                      status;

   usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;

   ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_isr_count++));

   dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR;

   status = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_STS);
   
   status &= USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_INTR);

   if(status == 0) 
   {
       ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_empty_isr_count++));
       return;
   } /* Endif */
/*
    USB_printf("USB_ISR: FRINDEX=0x%x, status=0x%x, PORTSC=0x%x, EP_SETUP=0x%x, EP_COMPLETE=0x%x\n", 
                    USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_FRINDEX),
                    status, 
                    USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]),
                    USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPT_SETUP_STAT),
                    USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCOMPLETE) );
*/
   /* Clear all the interrupts occured */
   dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_STS = USB_32BIT_LE(status);
   
   if (status & EHCI_STS_ERR) 
   {
        usb_dev_ptr->ERROR_STATE = (status & 0xFFFF);

        _usb_dci_vusb20_process_error((pointer)usb_dev_ptr);
        /* do tr_complete if no STS_INT set */
        if( (status & EHCI_STS_INT) == 0)
            _usb_dci_vusb20_process_tr_complete((pointer)usb_dev_ptr);

        /*USB_printf("USB process error: status=0x%x\n", status);*/
   } /* Endif */

   if (status & EHCI_STS_RESET) 
   {
       _usb_dci_vusb20_process_reset((pointer)usb_dev_ptr);
   } /* Endif */
   
   if (status & EHCI_STS_PORT_CHANGE) 
   {
      _usb_dci_vusb20_process_port_change((pointer)usb_dev_ptr);
   } /* Endif */
      
   if (status & EHCI_STS_SOF) 
   {
      _usb_dci_vusb20_process_SOF((pointer)usb_dev_ptr);
   } /* Endif */
   
   if (status & EHCI_STS_INT) 
   {
      _usb_dci_vusb20_process_tr_complete((pointer)usb_dev_ptr);
   } /* Endif */
   
    if (status & EHCI_STS_SUSPEND) 
    {
        _usb_dci_vusb20_process_suspend((pointer)usb_dev_ptr);
    } /* Endif */

} /* EndBody */

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_process_reset
*  Returned Value : None
*  Comments       :
*        Services reset interrupt
*
*END*-----------------------------------------------------------------*/
void _usb_dci_vusb20_process_reset
   (
      /* [IN] the USB_dev_initialize state structure */
      _usb_device_handle         handle
   )
{ /* Body */
   USB_DEV_STATE_STRUCT_PTR     usb_dev_ptr;
   VUSB20_REG_STRUCT_PTR        dev_ptr;
   uint_32                      temp;
   volatile unsigned long       timeout;

   usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;

   ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ISR, "process_reset\n");
   ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_reset_count++));
   
   dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR;

   /* Inform the application so that it can cancel all previously queued transfers */
   _usb_device_call_service(usb_dev_ptr, ARC_USB_SERVICE_BUS_RESET, 0, 0, 0, 0, 0);

#if defined(USB_UNDERRUN_WA)
    _usb_reset_send_queue();
#endif /* USB_UNDERRUN_WA */
   
   /* The address bits are past bit 25-31. Set the address */
   dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.DEVICE_ADDR &= ~USB_32BIT_LE(0xFE000000);
   
   /* Clear all the setup token semaphores */
   temp = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPT_SETUP_STAT);
   dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPT_SETUP_STAT = USB_32BIT_LE(temp);

   /* Clear all the endpoint complete status bits */   
   temp = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCOMPLETE);
   dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCOMPLETE = USB_32BIT_LE(temp);
   
    timeout = 0x10000;
    while (USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME) & 0xFFFFFFFF) 
    {
        timeout--;
        if(timeout <= 0)
        {
            USB_printf("%s: Timeout for ENDPTPRIME = 0x%x\n", __FUNCTION__, 
                (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME));
            break;
        }

      /* Wait until all ENDPTPRIME bits cleared */
    } /* Endif */
   
   /* Write 1s to the Flush register */
   dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTFLUSH = USB_32BIT_LE(0xFFFFFFFF);
   
    if( (usb_dev_ptr->ERROR_STATE == 0x0) &&
        (USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]) & 
                                                EHCI_PORTSCX_PORT_RESET) )
    {
        ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_RESET,
                    "USB Bus Reset: fri=0x%x, dev_ptr=%p, STATE=%d, PORTSC=0x%x, CMD=0x%x, ENDPT[0]=0x%x\n", 
                    USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_FRINDEX), 
                    usb_dev_ptr, usb_dev_ptr->USB_STATE,
                    USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]),
                    USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD),
                    USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[0]));

        usb_dev_ptr->BUS_RESETTING = TRUE;
        usb_dev_ptr->USB_STATE = ARC_USB_STATE_POWERED;
    } 
    else 
    { 
        USB_printf("USB Chip reinit: PORTSC=0x%x\n", 
                USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]));

        /* re-initialize */      
        _usb_dci_vusb20_chip_initialize((pointer)usb_dev_ptr);
        ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_INIT, "process_reset, Chip reinit hw\n");
    } /* Endif */
   
    _usb_device_call_service(usb_dev_ptr, ARC_USB_SERVICE_BUS_RESET, 1, 0, 0, 0, 0);

} /* EndBody */

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_process_suspend
*  Returned Value : None
*  Comments       :
*        Services suspend interrupt
*
*END*-----------------------------------------------------------------*/
void _usb_dci_vusb20_process_suspend
   (
      /* [IN] the USB_dev_initialize state structure */
      _usb_device_handle         handle
   )
{ /* Body */
    USB_DEV_STATE_STRUCT_PTR     usb_dev_ptr;
    VUSB20_REG_STRUCT_PTR        dev_ptr;
   
    usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;

    dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR;

    ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ISR, "process_suspend\n");
    ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_suspend_count++));
   
    usb_dev_ptr->USB_DEV_STATE_B4_SUSPEND = usb_dev_ptr->USB_STATE;
   
    usb_dev_ptr->USB_STATE = ARC_USB_STATE_SUSPEND;

    /* Inform the upper layers */
    _usb_device_call_service(usb_dev_ptr, ARC_USB_SERVICE_SLEEP, 0, 0, 0, 0, 0);
   
} /* EndBody */

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_process_SOF
*  Returned Value : None
*  Comments       :
*        Services SOF interrupt
*
*END*-----------------------------------------------------------------*/
void _usb_dci_vusb20_process_SOF
   (
      /* [IN] the USB_dev_initialize state structure */
      _usb_device_handle         handle
   )
{ /* Body */
   USB_DEV_STATE_STRUCT_PTR                     usb_dev_ptr;
   VUSB20_REG_STRUCT_PTR                        dev_ptr;

   ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ISR, "process_SOF\n");
   
   usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;
   dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR;

   /* Inform the upper layer */   
   _usb_device_call_service(usb_dev_ptr, ARC_USB_SERVICE_SOF, 0, 0, 0, 
      USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_FRINDEX), 0);

} /* EndBody */


/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_process_port_change
*  Returned Value : None
*  Comments       :
*        Services port change detect interrupt
*
*END*-----------------------------------------------------------------*/
void _usb_dci_vusb20_process_port_change
   (
      /* [IN] the USB_dev_initialize state structure */
      _usb_device_handle         handle
   )
{ /* Body */
    USB_DEV_STATE_STRUCT_PTR                     usb_dev_ptr;
    VUSB20_REG_STRUCT_PTR                        dev_ptr;

    usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;

    ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ISR, "process_port_change\n");
    ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_port_change_count++));

    dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR;
/*
    USB_printf("port_change: PORTSC=0x%x, DTD_ENTRIES=%d, XD_ENTRIES=%d\n", 
                USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]),
                usb_dev_ptr->DTD_ENTRIES, usb_dev_ptr->XD_ENTRIES);
*/
    if (usb_dev_ptr->BUS_RESETTING) 
    {
        /* Bus reset operation complete */
        usb_dev_ptr->BUS_RESETTING = FALSE;
    } /* Endif */
   
    if (!(USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]) & 
                                                EHCI_PORTSCX_PORT_RESET)) 
    {
        /* Get the speed */
        if (USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]) & 
                                            EHCI_PORTSCX_PORT_HIGH_SPEED) 
        {
            usb_dev_ptr->SPEED = ARC_USB_SPEED_HIGH;
        } 
        else 
        {
            usb_dev_ptr->SPEED = ARC_USB_SPEED_FULL;
        } /* Endif */
/*
        USB_printf("USB %s speed device detected\n", 
                (usb_dev_ptr->SPEED == ARC_USB_SPEED_HIGH) ? "High" : "Full");
*/
        /* Inform the upper layers of the speed of operation */      
        _usb_device_call_service(usb_dev_ptr, ARC_USB_SERVICE_SPEED_DETECTION, 0, 0, 
                                0, usb_dev_ptr->SPEED, 0);
    } /* Endif */
      
    if (USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]) & 
                                                EHCI_PORTSCX_PORT_SUSPEND) 
    {   
        usb_dev_ptr->USB_DEV_STATE_B4_SUSPEND = usb_dev_ptr->USB_STATE;
        usb_dev_ptr->USB_STATE = ARC_USB_STATE_SUSPEND;

        /* Inform the upper layers */
        USB_printf("USB suspend\n");
        _usb_device_call_service(usb_dev_ptr, ARC_USB_SERVICE_SUSPEND, 0, 0, 0, 0, 0);
    } /* Endif */
   
    if (!(USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]) & EHCI_PORTSCX_PORT_SUSPEND) 
                    && (usb_dev_ptr->USB_STATE == ARC_USB_STATE_SUSPEND)) 
    {
        USB_printf("USB resume\n");
        usb_dev_ptr->USB_STATE = usb_dev_ptr->USB_DEV_STATE_B4_SUSPEND;
        /* Inform the upper layers */
        _usb_device_call_service(usb_dev_ptr, ARC_USB_SERVICE_RESUME, 0, 0, 0, 0, 0);

        ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_SUSPEND, "process_port_change, SUCCESSFUL, resumed\n");
        return;
    } /* Endif */
   
    usb_dev_ptr->USB_STATE = ARC_USB_STATE_DEFAULT;
      
} /* EndBody */

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_process_error
*  Returned Value : None
*  Comments       :
*        Services error interrupt
*
*END*-----------------------------------------------------------------*/
void _usb_dci_vusb20_process_error
   (
      /* [IN] the USB_dev_initialize state structure */
      _usb_device_handle         handle
   )
{ /* Body */
   USB_DEV_STATE_STRUCT_PTR          usb_dev_ptr;
   
   usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;
   
   /* Increment the error count */
   usb_dev_ptr->ERRORS++;
   
    ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ERROR, 
                "process_error_%d: state=0x%x\n", 
                    usb_dev_ptr->ERRORS, usb_dev_ptr->ERROR_STATE); 
} /* EndBody */

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_set_speed_full
*  Returned Value : None
*  Comments       :
*        Force the controller port in full speed mode.
*
*END*-----------------------------------------------------------------*/
void _usb_dci_vusb20_set_speed_full
   (
      /* [IN] the USB_dev_initialize state structure */
      _usb_device_handle         handle,
     
      /* The port number on the device */
      uint_8                     port_number
   )
{ /* Body */
   USB_DEV_STATE_STRUCT_PTR                     usb_dev_ptr;
   VUSB20_REG_STRUCT_PTR                        dev_ptr;
   uint_32                                      port_control;
   
   ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ANY, "FORCE set_speed_full\n");
   
   usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;
   dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR;

   port_control = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[port_number]);   
   port_control |= EHCI_PORTSCX_FORCE_FULL_SPEED_CONNECT;
   dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[port_number] = USB_32BIT_LE(port_control);
  
} /* EndBody */

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_suspend_phy
*  Returned Value : None
*  Comments       :
*        Suspends the PHY in low power mode
*
*END*-----------------------------------------------------------------*/
void _usb_dci_vusb20_suspend_phy
   (
      /* [IN] the USB_dev_initialize state structure */
      _usb_device_handle         handle,
     
      /* The port number on the device */
      uint_8                     port_number
   )
{ /* Body */
   USB_DEV_STATE_STRUCT_PTR                     usb_dev_ptr;
   VUSB20_REG_STRUCT_PTR                        dev_ptr;
   uint_32                                      port_control;
      
   ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_SUSPEND, "set_suspend_phy\n");
   
   usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;
   dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR;

   port_control = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[port_number]);   
   port_control |= EHCI_PORTSCX_PHY_CLOCK_DISABLE;
   dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[port_number] = USB_32BIT_LE(port_control);
   
} /* EndBody */


/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_set_address
*  Returned Value : None
*  Comments       :
*        Sets the newly assigned device address
*
*END*-----------------------------------------------------------------*/
void _usb_dci_vusb20_set_address
   (
      /* [IN] the USB_dev_initialize state structure */
      _usb_device_handle         handle,
     
      /* Address of the device assigned by the host */
      uint_8                     address
   )
{ /* Body */
   USB_DEV_STATE_STRUCT_PTR          usb_dev_ptr;
   VUSB20_REG_STRUCT_PTR             dev_ptr;

   ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ADDR, "set_address: address=%d\n",address);
   
   usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;
   dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR;
   
#ifdef SET_ADDRESS_HARDWARE_ASSISTANCE   
   /***********************************************************
   Hardware Rev 4.0 onwards have special assistance built in
   for handling the set_address command. As per the USB specs
   a device should be able to receive the response on a new
   address, within 2 msecs after status phase of set_address is
   completed. Since 2 mili second may be a very small time window
   (on high interrupt latency systems) before software could 
   come to the code below and write the device register,
   this routine will be called in advance when status phase of
   set_address is still not finished. The following line in the
   code will set the bit 24 to '1' and hardware will take
   the address and queue it in an internal buffer. From which
   it will use it to decode the next USB token. Please look
   at hardware rev details for the implementation of this
   assistance.
   
   Also note that writing bit 24 to 0x01 will not break
   any old hardware revs because it was an unused bit.
   ***********************************************************/
   /* The address bits are past bit 25-31. Set the address 
   also set the bit 24 to 0x01 to start hardware assitance*/
   dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.DEVICE_ADDR = 
      USB_32BIT_LE((uint_32)address << VUSBHS_ADDRESS_BIT_SHIFT) | 
      (0x01 << (VUSBHS_ADDRESS_BIT_SHIFT -1)); 
#else
   dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.DEVICE_ADDR = 
      USB_32BIT_LE((uint_32)address << VUSBHS_ADDRESS_BIT_SHIFT); 
#endif /* SET_ADDRESS_HARDWARE_ASSISTANCE */
   
   usb_dev_ptr->USB_STATE = ARC_USB_STATE_ADDRESS;
      
} /* EndBody */

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_get_setup_data
*  Returned Value : None
*  Comments       :
*        Reads the Setup data from the 8-byte setup buffer
*
*END*-----------------------------------------------------------------*/
void _usb_dci_vusb20_get_setup_data
   (
      /* [IN] the USB_dev_initialize state structure */
      _usb_device_handle         handle,
            
      /* [IN] the Endpoint number */
      uint_8                     ep_num,
            
      /* [OUT] address of the buffer to read the setup data into */
      uint_8_ptr                  buffer_ptr
   )
{ /* Body */
    USB_DEV_STATE_STRUCT_PTR                    usb_dev_ptr;
    volatile VUSB20_REG_STRUCT_PTR              dev_ptr;
    volatile VUSB20_EP_QUEUE_HEAD_STRUCT_PTR    ep_queue_head_ptr;
    volatile boolean                            read_safe = FALSE;                      
    volatile unsigned long                      timeout;

   
   usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;
   dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR;

   /* Get the endpoint queue head */
   ep_queue_head_ptr = (VUSB20_EP_QUEUE_HEAD_STRUCT_PTR)usb_dev_ptr->EP_QUEUE_HEAD_PTR + 
                                                                2*ep_num + ARC_USB_RECV;

   /********************************************************************
   CR 1219. Hardware versions 2.3+ have a implementation of tripwire 
   semaphore mechanism that requires that we read the contents of 
   QH safely by using the semaphore. Read the USBHS document to under
   stand how the code uses the semaphore mechanism. The following are
   the steps in brief
   
   1. USBCMD Write ‘1’ to Setup Tripwire in register.
   2. Duplicate contents of dQH.StatusBuffer into local software byte
      array.
   3  Read Setup TripWire in register. (if set - continue; if
      cleared goto 1.)
   4. Write '0' to clear Setup Tripwire in register.
   5. Process setup packet using local software byte array copy and
      execute status/handshake phases.
   
           
   ********************************************************************/
    timeout = 0x100000;
    while(!read_safe)
    {
        /*********************************************************
        start with setting the semaphores
        *********************************************************/

        dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD |= 
                                                USB_32BIT_LE(EHCI_CMD_SETUP_TRIPWIRE_SET);

        /* Copy the setup packet to private buffer */
        USB_memcopy((uint_8_ptr)ep_queue_head_ptr->SETUP_BUFFER, buffer_ptr, 8);

        /*********************************************************
        If setup tripwire semaphore is cleared by hardware it means
        that we have a danger and we need to restart.
        else we can exit out of loop safely.
        *********************************************************/
        if(USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD) &
                                                   EHCI_CMD_SETUP_TRIPWIRE_SET)
        {
            read_safe = TRUE; /* we can proceed exiting out of loop*/
        }
        if(timeout <= 0)
        {
            USB_printf("%s: Timeout for SETUP_TRIPWIRE = 0x%x\n", __FUNCTION__, 
                        (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD));
            break;
        }
    }
    ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_read_setup_count++));
  
   /*********************************************************
   Clear the semaphore bit now
   *********************************************************/
   dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD &=
                                    USB_32BIT_LE(EHCI_CMD_SETUP_TRIPWIRE_CLEAR);                         
   
   /* Clear the bit in the ENDPTSETUPSTAT */
   dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPT_SETUP_STAT = USB_32BIT_LE(1 << ep_num);
   
} /* EndBody */

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_init_endpoint
*  Returned Value : None
*  Comments       :
*        Initializes the specified endpoint and the endpoint queue head
*
*END*-----------------------------------------------------------------*/
uint_8 _usb_dci_vusb20_init_endpoint
   (
      /* [IN] the USB_dev_initialize state structure */
      _usb_device_handle         handle,
            
      /* [IN] the transaction descriptor address */
      XD_STRUCT_PTR              xd_ptr
   )
{ /* Body */
   USB_DEV_STATE_STRUCT_PTR                     usb_dev_ptr;
   VUSB20_REG_STRUCT_PTR                        dev_ptr;
   VUSB20_EP_QUEUE_HEAD_STRUCT _PTR_            ep_queue_head_ptr;
   uint_32                                      val, bit_pos;
   
   usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;
   dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR;

   /* Get the endpoint queue head address */
   ep_queue_head_ptr = (VUSB20_EP_QUEUE_HEAD_STRUCT_PTR)usb_dev_ptr->EP_QUEUE_HEAD_PTR + 
                                                    2*xd_ptr->EP_NUM + xd_ptr->BDIRECTION;
      
   bit_pos = (1 << (16 * xd_ptr->BDIRECTION + xd_ptr->EP_NUM));

   /* Check if the Endpoint is Primed */
   if ((!(USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME) & bit_pos)) && 
       (!(USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS) & bit_pos))) 
   { 
      /* Set the max packet length, interrupt on Setup and Mult fields */
      if (xd_ptr->EP_TYPE == ARC_USB_ISOCHRONOUS_ENDPOINT) 
      {
         /* Mult bit should be set for isochronous endpoints */
         ep_queue_head_ptr->MAX_PKT_LENGTH = USB_32BIT_LE((xd_ptr->WMAXPACKETSIZE << 16) | 
            ((xd_ptr->MAX_PKTS_PER_UFRAME ?  xd_ptr->MAX_PKTS_PER_UFRAME : 1) << 
            VUSB_EP_QUEUE_HEAD_MULT_POS));
      } 
      else 
      {
         if (xd_ptr->EP_TYPE != ARC_USB_CONTROL_ENDPOINT) 
         {
             /* BULK or INTERRUPT */
            ep_queue_head_ptr->MAX_PKT_LENGTH = USB_32BIT_LE((xd_ptr->WMAXPACKETSIZE << 16) | 
               (xd_ptr->DONT_ZERO_TERMINATE ? VUSB_EP_QUEUE_HEAD_ZERO_LEN_TER_SEL : 0));
         } 
         else 
         {
             /* CONTROL */
            ep_queue_head_ptr->MAX_PKT_LENGTH = USB_32BIT_LE((xd_ptr->WMAXPACKETSIZE << 16) | 
                                                    VUSB_EP_QUEUE_HEAD_IOS);
         } /* Endif */
      } /* Endif */
      
      /* Enable the endpoint for Rx or Tx and set the endpoint type */
      val = dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[xd_ptr->EP_NUM];
      if(xd_ptr->BDIRECTION  == ARC_USB_SEND)
      {
          val &= ~(USB_32BIT_LE(EHCI_EPCTRL_TX_ALL_MASK));
          val |= USB_32BIT_LE((EHCI_EPCTRL_TX_ENABLE | EHCI_EPCTRL_TX_DATA_TOGGLE_RST) | 
                          (xd_ptr->EP_TYPE << EHCI_EPCTRL_TX_EP_TYPE_SHIFT));
      }
      else
      {
          val &= ~(USB_32BIT_LE(EHCI_EPCTRL_RX_ALL_MASK));
          val |= USB_32BIT_LE((EHCI_EPCTRL_RX_ENABLE | EHCI_EPCTRL_RX_DATA_TOGGLE_RST) | 
                          (xd_ptr->EP_TYPE << EHCI_EPCTRL_RX_EP_TYPE_SHIFT));
      }
      dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[xd_ptr->EP_NUM] = val;
      
      /* Implement Guideline (GL# USB-7) The unused endpoint type must  */
      /* be programmed to bulk.                                         */
      if( (dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[xd_ptr->EP_NUM] & 
            USB_32BIT_LE(EHCI_EPCTRL_RX_ENABLE)) == 0)
      {
          dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[xd_ptr->EP_NUM] |= 
              USB_32BIT_LE(ARC_USB_BULK_ENDPOINT << EHCI_EPCTRL_RX_EP_TYPE_SHIFT);
      }

      if( (dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[xd_ptr->EP_NUM] & 
            USB_32BIT_LE(EHCI_EPCTRL_TX_ENABLE)) == 0)
      {
          dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[xd_ptr->EP_NUM] |= 
              USB_32BIT_LE(ARC_USB_BULK_ENDPOINT << EHCI_EPCTRL_TX_EP_TYPE_SHIFT);
      }

      ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_INIT,
                    "init ep #%d %s: type=0x%x, EPCTRLX=0x%x, SETUP=0x%x, PRIME=0x%x, STATUS=0x%x, COMPL=0x%x\n", 
                        xd_ptr->EP_NUM, xd_ptr->BDIRECTION ? "SEND" : "RECV", xd_ptr->EP_TYPE, 
                        (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[xd_ptr->EP_NUM]),
                        (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPT_SETUP_STAT),
                        (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME),
                        (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS),
                        (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCOMPLETE) );
   } 
   else 
   { 
      USB_printf("ep=%d %s: Init ERROR: ENDPTPRIME=0x%x, ENDPTSTATUS=0x%x, bit_pos=0x%x\n",
                (unsigned)xd_ptr->EP_NUM, xd_ptr->BDIRECTION ? "SEND" : "RECV",
                (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME),
                (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS), 
                (unsigned)bit_pos);
        return USBERR_EP_INIT_FAILED;
   } /* Endif */
      
   return USB_OK;
   
} /* EndBody */

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_get_transfer_status
*  Returned Value : USB_OK or error code
*  Comments       :
*        Gets the status of a transfer
*
*END*-----------------------------------------------------------------*/
uint_8 _usb_dci_vusb20_get_transfer_status
   (
      /* [IN] the USB_dev_initialize state structure */
      _usb_device_handle         handle,
     
      /* [IN] the Endpoint number */
      uint_8                     ep_num,
            
      /* [IN] direction */
      uint_8                     direction
   )
{ /* Body */
   USB_DEV_STATE_STRUCT_PTR                     usb_dev_ptr;
   VUSB20_EP_TR_STRUCT_PTR                      dTD_ptr;
   XD_STRUCT_PTR                                xd_ptr;
   uint_8                                       status;
   
   usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;
  
   /* Unlink the dTD */
   dTD_ptr = usb_dev_ptr->EP_DTD_HEADS[2*ep_num + direction];

   if (dTD_ptr) 
   {
      /* Get the transfer descriptor for the dTD */
      xd_ptr = (XD_STRUCT_PTR)dTD_ptr->SCRATCH_PTR->XD_FOR_THIS_DTD;
      status = xd_ptr->BSTATUS;
   } 
   else 
   {
      status = ARC_USB_STATUS_IDLE;
   } /* Endif */
   
   return (status);

} /* EndBody */

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_get_transfer_details
*  Returned Value : pointer to structure that has details for transfer
*        Gets the status of a transfer
*
*END*-----------------------------------------------------------------*/
XD_STRUCT_PTR  _usb_dci_vusb20_get_transfer_details
   (
      /* [IN] the USB_dev_initialize state structure */
      _usb_device_handle         handle,
     
      /* [IN] the Endpoint number */
      uint_8                     ep_num,
            
      /* [IN] direction */
      uint_8                     direction
   )
{ /* Body */
   USB_DEV_STATE_STRUCT_PTR                     usb_dev_ptr;
   VUSB20_REG_STRUCT_PTR                        dev_ptr;
   VUSB20_EP_TR_STRUCT_PTR                      dTD_ptr, temp_dTD_ptr;
   XD_STRUCT_PTR                                xd_ptr;
   uint_32                                      temp, remaining_bytes;
   VUSB20_EP_QUEUE_HEAD_STRUCT_PTR              ep_queue_head_ptr;

   
   usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;
   dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR;
   temp = (2*ep_num + direction);

   /* get a pointer to QH for this endpoint */   
   ep_queue_head_ptr = (VUSB20_EP_QUEUE_HEAD_STRUCT_PTR)usb_dev_ptr->EP_QUEUE_HEAD_PTR + temp;

   ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_TRACE, "get_transfer_details\n");
   
   /* Unlink the dTD */
   dTD_ptr = usb_dev_ptr->EP_DTD_HEADS[2*ep_num + direction];

   if (dTD_ptr) 
   {      
      /* Get the transfer descriptor for the dTD */
      xd_ptr = (XD_STRUCT_PTR)dTD_ptr->SCRATCH_PTR->XD_FOR_THIS_DTD;
      if(!xd_ptr) return NULL;
      
      /* Initialize the transfer length field */
      xd_ptr->WSOFAR =0;
      remaining_bytes =0;
      
      /*if length of this transfer is greater than 20K
      we have multiple DTDs to count */
      if(xd_ptr->WTOTALLENGTH > VUSB_EP_MAX_LENGTH_TRANSFER)
      {
         /* it is a valid DTD. We should parse all DTDs for this XD
         and find the total bytes used so far */
         temp_dTD_ptr = dTD_ptr;
      
         /*loop through the list of DTDS until an active DTD is found
         or list has finished */
         while(!(USB_32BIT_LE(dTD_ptr->NEXT_TR_ELEM_PTR) & VUSBHS_TD_NEXT_TERMINATE))         
         {
            
            /**********************************************************
            If this DTD has been overlayed, we take the actual length 
            from QH.
            **********************************************************/

            if ((uint_32)(USB_32BIT_LE(ep_queue_head_ptr->CURR_DTD_PTR) & VUSBHS_TD_ADDR_MASK) ==
                                     USB_DTD_VIRT_TO_PHYS(usb_dev_ptr, temp_dTD_ptr) )
            {
                remaining_bytes += 
                  ((USB_32BIT_LE(ep_queue_head_ptr->SIZE_IOC_INT_STS) & VUSB_EP_TR_PACKET_SIZE) >> 16);
            }
            else
            {
               /* take the length from DTD itself */
                remaining_bytes += 
                  ((USB_32BIT_LE(temp_dTD_ptr->SIZE_IOC_STS) & VUSB_EP_TR_PACKET_SIZE) >> 16);
            }
   
            dTD_ptr = temp_dTD_ptr;
             
            /* Get the address of the next dTD */
            temp_dTD_ptr = (VUSB20_EP_TR_STRUCT_PTR)USB_DTD_PHYS_TO_VIRT(usb_dev_ptr, 
                                (uint_32)(USB_32BIT_LE(temp_dTD_ptr->NEXT_TR_ELEM_PTR) & VUSBHS_TD_ADDR_MASK) );
         }
         xd_ptr->WSOFAR = xd_ptr->WTOTALLENGTH - remaining_bytes;
      }
      else
      {
         /*look at actual length from QH*/
         xd_ptr->WSOFAR = xd_ptr->WTOTALLENGTH - 
            ((USB_32BIT_LE(ep_queue_head_ptr->SIZE_IOC_INT_STS) & VUSB_EP_TR_PACKET_SIZE) >> 16);         
      }      
   } 
   else 
   {
      xd_ptr = NULL;
   } /* Endif */
   
   return (xd_ptr);

} /* EndBody */

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_deinit_endpoint
*  Returned Value : None
*  Comments       :
*        Disables the specified endpoint and the endpoint queue head
*
*END*-----------------------------------------------------------------*/
uint_8 _usb_dci_vusb20_deinit_endpoint
   (
      /* [IN] the USB_dev_initialize state structure */
      _usb_device_handle         handle,
            
      /* [IN] the Endpoint number */
      uint_8                     ep_num,
            
      /* [IN] direction */
      uint_8                     direction
   )
{ /* Body */
    USB_DEV_STATE_STRUCT_PTR        usb_dev_ptr;
    VUSB20_REG_STRUCT_PTR           dev_ptr;
    VUSB20_EP_QUEUE_HEAD_STRUCT*    ep_queue_head_ptr;
    uint_32                         bit_pos;
    uint_8                          status = USB_OK;
   
    usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;
    dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR;
   
    /* Get the endpoint queue head address */
    ep_queue_head_ptr = (VUSB20_EP_QUEUE_HEAD_STRUCT_PTR)usb_dev_ptr->EP_QUEUE_HEAD_PTR +
                                                                    (2*ep_num + direction);
      
    bit_pos = (1 << (16 * direction + ep_num));

    ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_INIT,
               "deinit ep #%d-%s: bit_pos=0x%x, EPCTRLX=0x%x, SETUP=0x%x, PRIME=0x%x, STATUS=0x%x, COMPL=0x%x\n", 
                   ep_num, direction ? "SEND" : "RECV", bit_pos,
                   (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[ep_num]),
                   (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPT_SETUP_STAT),
                   (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME),
                   (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS),
                   (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCOMPLETE) );
      
    /* Check if the Endpoint is Primed */
    if( ((USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME) & bit_pos)) || 
        ((USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS) & bit_pos)) ) 
    { 
        USB_printf("ep=%d %s: Deinit ERROR: ENDPTPRIME=0x%x, ENDPTSTATUS=0x%x, bit_pos=0x%x\n",
                (unsigned)ep_num, direction ? "SEND" : "RECV",
                (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME),
                (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS), 
                (unsigned)bit_pos);
        status = USBERR_EP_DEINIT_FAILED;
    }

    /* Reset the max packet length and the interrupt on Setup */
    ep_queue_head_ptr->MAX_PKT_LENGTH = 0;
      
    /* Disable the endpoint for Rx or Tx and reset the endpoint type */
    dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[ep_num] &= 
         USB_32BIT_LE( ~((direction ? EHCI_EPCTRL_TX_ENABLE : EHCI_EPCTRL_RX_ENABLE) | 
                        (direction ? EHCI_EPCTRL_TX_TYPE : EHCI_EPCTRL_RX_TYPE)));
      
   return status;
}


/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_shutdown
*  Returned Value : None
*  Comments       :
*        Shuts down the VUSB_HS Device
*
*END*-----------------------------------------------------------------*/
void _usb_dci_vusb20_shutdown
   (
      /* [IN] the USB_dev_initialize state structure */
      _usb_device_handle         handle
   )
{ /* Body */
    USB_DEV_STATE_STRUCT_PTR                     usb_dev_ptr;
    VUSB20_REG_STRUCT_PTR                        dev_ptr;
   
    usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;
    dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR;
   
    /* Disable interrupts */
    dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_INTR &= 
      ~(USB_32BIT_LE(EHCI_INTR_INT_EN | EHCI_INTR_ERR_INT_EN |
      EHCI_INTR_PORT_CHANGE_DETECT_EN | EHCI_INTR_RESET_EN));
      
    USB_uncached_memfree(usb_dev_ptr->EP_QUEUE_HEAD_BASE, 
                        usb_dev_ptr->EP_QUEUE_HEAD_SIZE, 
                        usb_dev_ptr->EP_QUEUE_HEAD_PHYS);

    USB_uncached_memfree(usb_dev_ptr->DTD_BASE_PTR, 
                        usb_dev_ptr->DTD_SIZE, 
                        usb_dev_ptr->DTD_BASE_PHYS);

    USB_memfree(usb_dev_ptr->SCRATCH_STRUCT_BASE);

    USB_printf("USB shutdown: usb_dev_ptr=%p\n", usb_dev_ptr);

   /* Reset the Run the bit in the command register to stop VUSB */
   dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD &= ~USB_32BIT_LE(EHCI_CMD_RUN_STOP);
   
   /* Reset the controller to get default values */
   dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD = USB_32BIT_LE(EHCI_CMD_CTRL_RESET);
              
} /* EndBody */
/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_stop
*  Returned Value : None
*  Comments       :
*        Stop USB device controller
*
*END*-----------------------------------------------------------------*/
void _usb_dci_vusb20_stop(_usb_device_handle handle)
{
   USB_DEV_STATE_STRUCT_PTR     usb_dev_ptr;
   VUSB20_REG_STRUCT_PTR        dev_ptr;
   
   usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;
   dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR;

    /* Disable interrupts */
   dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_INTR &= 
            ~(USB_32BIT_LE(EHCI_INTR_INT_EN | EHCI_INTR_ERR_INT_EN |
                           EHCI_INTR_PORT_CHANGE_DETECT_EN | EHCI_INTR_RESET_EN));

   /* Reset the Run the bit in the command register to stop VUSB */
   dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD &= ~USB_32BIT_LE(EHCI_CMD_RUN_STOP);
}


/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_vusb20_start
*  Returned Value : None
*  Comments       :
*        Start USB device controller
*
*END*-----------------------------------------------------------------*/
void _usb_dci_vusb20_start(_usb_device_handle handle)
{
   USB_DEV_STATE_STRUCT_PTR     usb_dev_ptr;
   VUSB20_REG_STRUCT_PTR        dev_ptr;
   
   usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;
   dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR;

      /* Enable interrupts */
   dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_INTR = USB_32BIT_LE(
                           EHCI_INTR_INT_EN
                         | EHCI_INTR_ERR_INT_EN 
                         | EHCI_INTR_PORT_CHANGE_DETECT_EN 
                         | EHCI_INTR_RESET_EN 
                         | EHCI_INTR_DEVICE_SUSPEND
                       /*  
                         | EHCI_INTR_SOF_UFRAME_EN 
                        */
                         );
   
   usb_dev_ptr->USB_STATE = ARC_USB_STATE_UNKNOWN;
   
   /* Set the Run bit in the command register */
   dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD = USB_32BIT_LE(EHCI_CMD_RUN_STOP);
}

/* EOF */

