/* dwc controller pcd interrupt drivers  */
/*
 * (C) Copyright 2010 Amlogic, Inc
 *
 * Victor Wan, victor.wan@amlogic.com, 
 * 2010-03-30 @ Shanghai
 *
 */
#include "platform.h"
#include "usb_ch9.h"
#include "dwc_pcd.h"
#include "dwc_pcd_irq.h"
//#define flush_cpu_cache()
extern void do_gadget_setup( pcd_struct_t *_pcd, struct usb_ctrlrequest * ctrl);
extern void do_vendor_request( pcd_struct_t *_pcd, struct usb_ctrlrequest * ctrl);
extern void do_vendor_out_complete( pcd_struct_t *_pcd, struct usb_ctrlrequest * ctrl);
extern void do_bulk_complete( pcd_struct_t *_pcd);
extern void do_modify_memory(u16 opcode,char * inbuff);

static void ep0_out_start(void);
static int32_t ep0_complete_request( pcd_struct_t * pcd);
/**
 * This function starts the Zero-Length Packet for the IN status phase
 * of a 2 stage control transfer. 
 */
static void do_setup_in_status_phase( pcd_struct_t *_pcd)
{
        dwc_ep_t *ep0 = &g_dwc_eps[0];
        if (_pcd->ep0state == EP0_STALL){
                return;
        }
                
        _pcd->ep0state = EP0_STATUS;
        
        DBG( "EP0 IN ZLP\n");
        ep0->xfer_len = 0;
        ep0->xfer_count = 0;
        ep0->is_in = 1;

        dwc_otg_ep_start_transfer( ep0 );

        /* Prepare for more SETUP Packets */
        ep0_out_start();

}
/**
 * This function starts the Zero-Length Packet for the OUT status phase
 * of a 2 stage control transfer. 
 */
static void do_setup_out_status_phase( pcd_struct_t *_pcd)
{
        dwc_ep_t *ep0 = &g_dwc_eps[0];
        if (_pcd->ep0state == EP0_STALL){
                return;
        }
        _pcd->ep0state = EP0_STATUS;
        
        /* Prepare for more SETUP Packets */
        //ep0_out_start( GET_CORE_IF(_pcd), _pcd );

        DBG( "EP0 OUT ZLP\n");
        ep0->xfer_len = 0;
        ep0->xfer_count = 0;
        ep0->is_in = 0;
        dwc_otg_ep_start_transfer( ep0 );

        /* Prepare for more SETUP Packets */
        ep0_out_start( );

} 
static void pcd_out_completed(pcd_struct_t *_pcd)
{
	do_vendor_out_complete(_pcd,(struct usb_ctrlrequest*)&_pcd->setup_pkt);
}
static void pcd_setup( pcd_struct_t *_pcd )
{

	struct usb_ctrlrequest	ctrl = _pcd->setup_pkt.req;
	dwc_ep_t	*ep0 = &g_dwc_eps[0];
	deptsiz0_data_t doeptsize0 = { 0};

	if(_pcd->request_enable == 0)
		return;

//	_pcd->setup_pkt.d32[0] = 0;
//	_pcd->setup_pkt.d32[1] = 0;
	unsigned short status = 0;
	_pcd->request_enable = 0;

        doeptsize0.d32 = dwc_read_reg32( DWC_REG_OUT_EP_TSIZE(0));

        if (ctrl.bRequestType & USB_DIR_IN) {        
                ep0->is_in = 1;
                _pcd->ep0state = EP0_IN_DATA_PHASE;
        } else {
                ep0->is_in = 0;
                _pcd->ep0state = EP0_OUT_DATA_PHASE;
        }


	if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) {
		/* handle non-standard (class/vendor) requests in the gadget driver */
		//do_gadget_setup(_pcd, &ctrl );
		DBG("Vendor requset\n");
		do_vendor_request(_pcd, &ctrl );
		dwc_otg_ep_req_start(_pcd,0);
		return;
	}

        /** @todo NGS: Handle bad setup packet? */

        switch (ctrl.bRequest) {

        case USB_REQ_GET_STATUS:

                switch (ctrl.bRequestType & USB_RECIP_MASK) {
                case USB_RECIP_DEVICE:
                        status = 0x1; /* Self powered */
                        status |= 0;//_pcd->remote_wakeup_enable << 1; 
                        break;
		}
		_pcd->buf = (char *)&status;
		_pcd->length = 2;
		dwc_otg_ep_req_start(_pcd,0);
		break;
#if 0
                case USB_RECIP_INTERFACE:
                        *status = 0;
                        break;                        

                case USB_RECIP_ENDPOINT:
                        ep = get_ep_by_addr(_pcd, ctrl.wIndex);
                        if ( ep == 0 || ctrl.wLength > 2) {
                                ep0_do_stall(_pcd, -EOPNOTSUPP);
                                return;
                        }
                        /** @todo check for EP stall */
                        *status = ep->stopped;
                        break;
                }
                _pcd->ep0_pending = 1;

                ep0->dwc_ep.start_xfer_buff = (uint8_t *)status;
                ep0->dwc_ep.xfer_buff = (uint8_t *)status;
                ep0->dwc_ep.dma_addr = _pcd->status_buf_dma_handle;
                ep0->dwc_ep.xfer_len = 2;
                ep0->dwc_ep.xfer_count = 0;
                ep0->dwc_ep.total_len = ep0->dwc_ep.xfer_len;
                dwc_otg_ep0_start_transfer( GET_CORE_IF(_pcd), &ep0->dwc_ep );
                break;

        case USB_REQ_CLEAR_FEATURE:
                do_clear_feature( _pcd );
                break;

        case USB_REQ_SET_FEATURE:
                do_set_feature( _pcd );
                break;
 #endif               
        case USB_REQ_SET_ADDRESS:
                if (ctrl.bRequestType == USB_RECIP_DEVICE) {
                        dcfg_data_t dcfg = { 0 };

			    //DBG("Set address: %d\n",ctrl.wValue);
                        dcfg.b.devaddr = ctrl.wValue;
                        dwc_modify_reg32(DWC_REG_DCFG,0, dcfg.d32);
                        do_setup_in_status_phase( _pcd );
                        return;
                }
                break;

        case USB_REQ_SET_INTERFACE:
        case USB_REQ_SET_CONFIGURATION:
                _pcd->request_config = 1;   /* Configuration changed */
               // do_gadget_setup(_pcd, &ctrl );
               // break;
                
        default:
                /* Call the Gadget Driver's setup functions */        
                do_gadget_setup(_pcd, &ctrl );
                dwc_otg_ep_req_start(_pcd,0);
		break;
        }
}

/**
 * This function handles EP0 Control transfers.  
 *
 * The state of the control tranfers are tracked in
 * <code>ep0state</code>.
 * is_in : 1 -- IN Trans
 * is_in : 0 -- OUT/SETUP Trans
 */
static void handle_ep0( int is_in )
{
	pcd_struct_t * _pcd = &this_pcd;
	dwc_ep_t * ep0 = &g_dwc_eps[0];
	
        switch (_pcd->ep0state){
        case EP0_DISCONNECT:
		  DBG("EP0 DISCONNECT\n");
                break;
                
	case EP0_IDLE:
                _pcd->request_config = 0;
                DBG("Enter PCD Setup()\n");
                pcd_setup( _pcd );
                break;

	case EP0_IN_DATA_PHASE:
           
		if (ep0->xfer_count < ep0->total_len) {
			DBG("FIX ME!! dwc_otg_ep0_continue_transfer!\n");
			//dwc_otg_ep0_continue_transfer ( GET_CORE_IF(_pcd), &ep0->dwc_ep );
		}
		else {
			ep0_complete_request( _pcd );
		}
                break;

	case EP0_OUT_DATA_PHASE:
                ep0_complete_request(_pcd );
		   pcd_out_completed(_pcd);
                break;
                

	case EP0_STATUS:

                ep0_complete_request( _pcd );
                _pcd->ep0state = EP0_IDLE;
                ep0->stopped = 1;
                ep0->is_in = 0;  /* OUT for next SETUP */
		
                break;

        case EP0_STALL:
                ERR("EP0 STALLed, should not get here pcd_setup()\n");
                break;
        }
}

/**
 * This function completes the request for the EP.  If there are
 * additional requests for the EP in the queue they will be started.
 */
static void complete_ep( int ep_num,int is_in )
{
	deptsiz_data_t deptsiz;
	pcd_struct_t *pcd = &this_pcd;   
	dwc_ep_t *ep = &g_dwc_eps[ep_num];

	if (is_in) {
		
		deptsiz.d32 = dwc_read_reg32(DWC_REG_IN_EP_TSIZE(ep_num));
		if (deptsiz.b.xfersize == 0 && deptsiz.b.pktcnt == 0 &&
                    ep->xfer_count == ep->xfer_len) {

			ep->start_xfer_buff = 0;
			ep->xfer_buff = 0;
			ep->xfer_len = 0;
		} 
	} 
	else {/* OUT Transfer */

		deptsiz.d32 = dwc_read_reg32(DWC_REG_OUT_EP_TSIZE(ep_num));

		pcd->xfer_len = ep->xfer_count;

		ep->start_xfer_buff = 0;
		ep->xfer_buff = 0;
		ep->xfer_len = 0;
	}
	do_bulk_complete(pcd);
}
/**
 * This function completes the ep0 control transfer.
 */
static int32_t ep0_complete_request( pcd_struct_t * pcd)
{

        deptsiz0_data_t deptsiz;
        int is_last = 0;
	 dwc_ep_t* ep = &g_dwc_eps[0];

	 DBG("ep0_complete_request()\n");
        if (pcd->ep0state == EP0_STATUS) {
                is_last = 1;                        
        } else if (ep->xfer_len == 0) {
		ep->xfer_len = 0;
		ep->xfer_count = 0;
		ep->sent_zlp = 1;
		dwc_otg_ep_start_transfer( ep );
		return 1;
	}
	else if (ep->is_in) {
		deptsiz.d32 = dwc_read_reg32(DWC_REG_IN_EP_TSIZE(0) );
		if (deptsiz.b.xfersize == 0) {
			/* Is a Zero Len Packet needed? */
			do_setup_out_status_phase(pcd);
		}
        } else {
			/* ep0-OUT */
			do_setup_in_status_phase(pcd);
	}
        
        /* Complete the request */
        if (is_last) {
                ep->start_xfer_buff = 0;
                ep->xfer_buff = 0;
                ep->xfer_len = 0;
                return 1;
        }
        return 0;       
}
/** 
 * This function reads a packet from the Rx FIFO into the destination
 * buffer.  To read SETUP data use dwc_otg_read_setup_packet.
 *
 * @param _dest   Destination buffer for the packet.
 * @param _bytes  Number of bytes to copy to the destination.
 */
void dwc_otg_read_packet(uint8_t *_dest, uint16_t _bytes)
{
	int i;
	uint32_t buffer =0;
	uint8_t*  pbyte = _dest;
	/**
	 * @todo Account for the case where _dest is not dword aligned. This
	 * requires reading data from the FIFO into a uint32_t temp buffer,
	 * then moving it into the data buffer.
	 */
	//DBG("dwc_otg_read_packet() dest: %p, len: %d\n",_dest,_bytes);
	if(((unsigned int)_dest &0x3) == 0)
		for(i=0; i<_bytes; i+=4){
			*(uint32_t*)_dest = dwc_read_reg32(DWC_REG_DATA_FIFO_START);
			_dest+=4;
		}
	else
		for (i=0; i<_bytes; i++) {
			if((i % 4) ==0)
				buffer =dwc_read_reg32(DWC_REG_DATA_FIFO_START);				

			*(uint8_t*)pbyte ++ = buffer;
			buffer >>= 8;
		}

	return;
}

/**
 * This function writes a packet into the Tx FIFO associated with the
 * EP.  For non-periodic EPs the non-periodic Tx FIFO is written.  For
 * periodic EPs the periodic Tx FIFO associated with the EP is written
 * with all packets for the next micro-frame.
 *
 * @param _core_if Programming view of DWC_otg controller.
 * @param _ep The EP to write packet for.
 * @param _dma Indicates if DMA is being used.
 */
void dwc_otg_ep_write_packet( dwc_ep_t *_ep)
{
	/**
	 * The buffer is padded to DWORD on a per packet basis in
	 * slave/dma mode if the MPS is not DWORD aligned.  The last
	 * packet, if short, is also padded to a multiple of DWORD.
	 *
	 * ep->xfer_buff always starts DWORD aligned in memory and is a 
	 * multiple of DWORD in length
	 *
	 * ep->xfer_len can be any number of bytes
	 *
	 * ep->xfer_count is a multiple of ep->maxpacket until the last 
	 *  packet
	 *
	 * FIFO access is DWORD */

	uint32_t i;
	uint32_t byte_count;
	uint32_t dword_count;
	uint32_t fifo;
        uint8_t *data_buff = _ep->xfer_buff;
        uint32_t temp_data ;

	//DBG("dwc_otg_ep_write_packet() : %d\n",_ep->xfer_len);
        if (_ep->xfer_count >= _ep->xfer_len) {
                //DWC_WARN("%s() No data for EP%d!!!\n", "dwc_otg_ep_write_packet", _ep->num);
                return;                
        }

	/* Find the byte length of the packet either short packet or MPS */
	if ((_ep->xfer_len - _ep->xfer_count) < _ep->maxpacket) {
		byte_count = _ep->xfer_len - _ep->xfer_count;
	}
	else {
		byte_count = _ep->maxpacket;
	}

	/* Find the DWORD length, padded by extra bytes as neccessary if MPS
	 * is not a multiple of DWORD */
	dword_count =  (byte_count + 3) / 4;


       //fifo = _core_if->data_fifo[_ep->num];
     fifo = DWC_REG_DATA_FIFO(_ep->num);


	for (i=0; i<dword_count; i++) {
		temp_data =get_unaligned(data_buff);
		dwc_write_reg32( fifo, temp_data );
		data_buff += 4;
	}


	_ep->xfer_count += byte_count;
        _ep->xfer_buff += byte_count;

	flush_cpu_cache();

}
/**
 * This function reads a setup packet from the Rx FIFO into the destination 
 * buffer.  This function is called from the Rx Status Queue Level (RxStsQLvl)
 * Interrupt routine when a SETUP packet has been received in Slave mode.
 *
 * @param _core_if Programming view of DWC_otg controller.
 * @param _dest Destination buffer for packet data.
 */
void dwc_otg_read_setup_packet(uint32_t *_dest)
{
	/* Get the 8 bytes of a setup transaction data */

	DBG("dwc_otg_read_setup_packet()\n");
	/* Pop 2 DWORDS off the receive data FIFO into memory */
	_dest[0] = dwc_read_reg32(DWC_REG_DATA_FIFO_START);
	_dest[1] = dwc_read_reg32(DWC_REG_DATA_FIFO_START);
}



/**
 * handle the IN EP disable interrupt.
 */
static void handle_in_ep_disable_intr(uint32_t _epnum)
{
#if 0
        deptsiz_data_t dieptsiz = { 0 };
        dctl_data_t dctl = { 0 };
        depctl_data_t diepctl = { 0 };
        dwc_ep_t *ep = &g_dwc_eps[_epnum];

        if (ep->stopped) {
                /* Flush the Tx FIFO */
                /** @todo NGS: This is not the correct FIFO */
                dwc_otg_flush_tx_fifo( core_if, 0 );
                /* Clear the Global IN NP NAK */
                dctl.d32 = 0;
                dctl.b.cgnpinnak = 1;
                dwc_modify_reg32(&dev_if->in_ep_regs[_epnum]->diepctl, 
                                 diepctl.d32, diepctl.d32);
                /* Restart the transaction */
                if (dieptsiz.b.pktcnt != 0 || 
                    dieptsiz.b.xfersize != 0) {
                        restart_transfer( _pcd, _epnum );
                }
        }
#endif
}

/**
 * Handler for the IN EP timeout handshake interrupt. 
 */
static void handle_in_ep_timeout_intr(uint32_t _epnum)
{

        dctl_data_t dctl = { 0 };
        dwc_ep_t *ep = &g_dwc_eps[_epnum];

        gintmsk_data_t intr_mask = {0};


        /* Disable the NP Tx Fifo Empty Interrrupt */

	intr_mask.b.nptxfempty = 1;
	dwc_modify_reg32( DWC_REG_GINTMSK, intr_mask.d32, 0);
        /** @todo NGS Check EP type.
         * Implement for Periodic EPs */
        /* 
         * Non-periodic EP 
         */
        /* Enable the Global IN NAK Effective Interrupt */
        intr_mask.b.ginnakeff = 1;
        dwc_modify_reg32( DWC_REG_GINTMSK, 0, intr_mask.d32);

        /* Set Global IN NAK */
        dctl.b.sgnpinnak = 1;
        dwc_modify_reg32( DWC_REG_DCTL,dctl.d32, dctl.d32);

        ep->stopped = 1;

}

///////////////////////////////////////////////////////////////////////
/**
 * This function handles the Rx Status Queue Level Interrupt, which
 * indicates that there is a least one packet in the Rx FIFO.  The
 * packets are moved from the FIFO to memory, where they will be
 * processed when the Endpoint Interrupt Register indicates Transfer
 * Complete or SETUP Phase Done.
 *
 * Repeat the following until the Rx Status Queue is empty:
 *   -#	Read the Receive Status Pop Register (GRXSTSP) to get Packet
 *     	info
 *   -#	If Receive FIFO is empty then skip to step Clear the interrupt
 *     	and exit
 *   -#	If SETUP Packet call dwc_otg_read_setup_packet to copy the
 *   	SETUP data to the buffer
 *   -#	If OUT Data Packet call dwc_otg_read_packet to copy the data
 *     	to the destination buffer
 */
int32_t dwc_otg_pcd_handle_rx_status_q_level_intr(void)
{
	gintmsk_data_t gintmask = { 0 };
	device_grxsts_data_t status;
	gintsts_data_t gintsts;
	dwc_ep_t *ep;

        
        DBG("dwc_otg_pcd_handle_rx_status_q_level_intr()\n");
        /* Disable the Rx Status Queue Level interrupt */
        gintmask.b.rxstsqlvl= 1;
        dwc_modify_reg32( DWC_REG_GINTMSK, gintmask.d32, 0);

        /* Get the Status from the top of the FIFO */
        status.d32 = dwc_read_reg32( DWC_REG_GRXSTSP);

        //DBG("rx status: ep%d, pktsts: %d\n",status.b.epnum,status.b.pktsts);

        /* Get pointer to EP structure */
	if(status.b.epnum == 0)
	{
		ep = &g_dwc_eps[0];
	}		
	else
	{
		ep = &g_dwc_eps[status.b.epnum];
	}
	 
        switch (status.b.pktsts) {
        case DWC_DSTS_GOUT_NAK:
                DBG( "Global OUT NAK\n");
                break;

        case DWC_STS_DATA_UPDT:
                DBG( "OUT Data Packet\n");
                if (status.b.bcnt && ep->xfer_buff){
                        /** @todo NGS Check for buffer overflow? */
                        dwc_otg_read_packet( ep->xfer_buff,status.b.bcnt);
                        ep->xfer_count += status.b.bcnt;
                        ep->xfer_buff += status.b.bcnt;
                }
                break;

        case DWC_STS_XFER_COMP:
                DBG("OUT Complete\n");
                break;

        case DWC_DSTS_SETUP_COMP:
		   DBG("SETUP Complete\n");
                break;

        case DWC_DSTS_SETUP_UPDT:
		   DBG("SETUP update\n");
                dwc_otg_read_setup_packet( this_pcd.setup_pkt.d32);
		   this_pcd.request_enable = 1;

                ep->xfer_count += status.b.bcnt;
                break;

        default:
                //DBG( "Invalid Packet Status (0x%0x)\n", status.b.pktsts);
                break;
                
        }

        /* Enable the Rx Status Queue Level interrupt */
        dwc_modify_reg32( DWC_REG_GINTMSK, 0, gintmask.d32);
	/* Clear interrupt */
	gintsts.d32 = 0;
	gintsts.b.rxstsqlvl = 1;
	dwc_write_reg32 ( DWC_REG_GINTSTS, gintsts.d32);

        return 1;
}


/**
 * This interrupt occurs when the non-periodic Tx FIFO is half-empty.
 * The active request is checked for the next packet to be loaded into
 * the non-periodic Tx FIFO.
 */
int32_t dwc_otg_pcd_handle_np_tx_fifo_empty_intr(void)
{
        gnptxsts_data_t txstatus = {0};
        gintsts_data_t gintsts;
        
        int epnum = 0;
        dwc_ep_t *ep = 0;
        uint32_t len = 0;
        int dwords;
	 depctl_data_t depctl;

	DBG("dwc_otg_pcd_handle_np_tx_fifo_empty_intr()\n");
        /* Get the epnum from the IN Token Learning Queue. */
	for (epnum=0; epnum < NUM_EP; epnum++)
	{

		ep = &g_dwc_eps[epnum];


		/* IN endpoint ? */
		if (epnum && !ep->is_in ) {
			continue;
	        } 
		depctl.d32 = dwc_read_reg32(DWC_REG_IN_EP_REG(epnum));
	       if(depctl.b.epena != 1)
		   	continue;

		 if(ep->type == DWC_OTG_EP_TYPE_INTR && ep->xfer_len == 0)
			continue;

		 flush_cpu_cache();
		 
	        len = ep->xfer_len - ep->xfer_count;
	        if (len > ep->maxpacket) {
	                len = ep->maxpacket;
	        }
	        dwords = (len + 3)/4;

		 //DBG("nptx: write data to fifo, ep%d , size %d\n",epnum,len);
	        /* While there is space in the queue and space in the FIFO and
	         * More data to tranfer, Write packets to the Tx FIFO */
	        txstatus.d32 = dwc_read_reg32( DWC_REG_GNPTXSTS );
	        while  (txstatus.b.nptxqspcavail > 0 &&
	                txstatus.b.nptxfspcavail > dwords &&
	                ep->xfer_count < ep->xfer_len) {
			                
			   flush_cpu_cache();           

			   /* Write the FIFO */
	                dwc_otg_ep_write_packet( ep );

	                len = ep->xfer_len - ep->xfer_count;
	                if (len > ep->maxpacket) {
	                        len = ep->maxpacket;
	                }
	                dwords = (len + 3)/4;
			   flush_cpu_cache(); 
			   //txstatus.d32 = dwc_read_reg32(DWC_REG_GNPTXSTS);
#if 1
			   /*
				  TODO:  Remove these code.
				  Because, if code break from "while"(Line427), an incomplete-in-trans will occour.
				  Then the tansfer will break.
			   */
			   int retry = 50000;	//retry times
			   while(retry--)
			   {
			       txstatus.d32 = dwc_read_reg32(DWC_REG_GNPTXSTS);
			   	if(txstatus.b.nptxqspcavail > 0  || //txstatus.b.nptxfspcavail <= dwords ||
					ep->xfer_count >= ep->xfer_len)
					break;
				else
				{
					flush_cpu_cache();   
				}
					
			   }
			   if(retry <= 0)
			   {
			   	//DWC_ERROR("TxFIFO FULL: Can't trans data to HOST !\n");
			   }

			   /* END todo */
#endif			   
	        }

	}

	/* Clear interrupt */
	gintsts.d32 = 0;
	gintsts.b.nptxfempty = 1;
	dwc_write_reg32 (DWC_REG_GINTSTS, gintsts.d32);
	
        return 1;
}
/**
 * Read the device status register and set the device speed in the
 * data structure.  
 * Set up EP0 to receive SETUP packets by calling dwc_ep0_activate.
 */
int32_t dwc_otg_pcd_handle_enum_done_intr(void)
{
	gintsts_data_t gintsts;
	gusbcfg_data_t gusbcfg;
	dsts_data_t dsts;
	depctl_data_t diepctl;
	depctl_data_t doepctl;
	dctl_data_t dctl ={0};        

	DBG("SPEED ENUM\n");

	/* Read the Device Status and Endpoint 0 Control registers */
	dsts.d32 = dwc_read_reg32(DWC_REG_DSTS);
	diepctl.d32 = dwc_read_reg32(DWC_REG_IN_EP_REG(0) );
	doepctl.d32 = dwc_read_reg32(DWC_REG_OUT_EP_REG(0));

	/* Set the MPS of the IN EP based on the enumeration speed */
	diepctl.b.mps = DWC_DEP0CTL_MPS_64;
	dwc_write_reg32(DWC_REG_IN_EP_REG(0) , diepctl.d32);

	/* Enable OUT EP for receive */
	doepctl.b.epena = 1;
	dwc_write_reg32(DWC_REG_OUT_EP_REG(0), doepctl.d32);

	dctl.b.cgnpinnak = 1;
	dwc_modify_reg32(DWC_REG_DCTL, dctl.d32, dctl.d32);
        
        if (this_pcd.ep0state == EP0_DISCONNECT) {
                this_pcd.ep0state = EP0_IDLE;
        } else if (this_pcd.ep0state == EP0_STALL) {
                this_pcd.ep0state = EP0_IDLE;
        }
	
	 this_pcd.ep0state = EP0_IDLE;


	/* Set USB turnaround time based on device speed and PHY interface. */
	gusbcfg.d32 = dwc_read_reg32(DWC_REG_GUSBCFG);
#if 0
	if (_pcd->gadget.speed == USB_SPEED_HIGH) {
		if (GET_CORE_IF(_pcd)->hwcfg2.b.hs_phy_type == DWC_HWCFG2_HS_PHY_TYPE_ULPI) {
			/* ULPI interface */
			gusbcfg.b.usbtrdtim = 9;
		}
		if (GET_CORE_IF(_pcd)->hwcfg2.b.hs_phy_type == DWC_HWCFG2_HS_PHY_TYPE_UTMI) {
			/* UTMI+ interface */
			if (GET_CORE_IF(_pcd)->core_params->phy_utmi_width == 16) {
				gusbcfg.b.usbtrdtim = 5;
			} else {
				gusbcfg.b.usbtrdtim = 9;
			}
		}
		if (GET_CORE_IF(_pcd)->hwcfg2.b.hs_phy_type == DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI) {
			/* UTMI+  OR  ULPI interface */
			if (gusbcfg.b.ulpi_utmi_sel == 1) {
				/* ULPI interface */
				gusbcfg.b.usbtrdtim = 9;
			} else {
				/* UTMI+ interface */
				if (GET_CORE_IF(_pcd)->core_params->phy_utmi_width == 16) {
					gusbcfg.b.usbtrdtim = 5;
				} else {
					gusbcfg.b.usbtrdtim = 9;
				}
			}
		}
	} else {
		/* Full or low speed */
		gusbcfg.b.usbtrdtim = 9;
	}
#else
	/* Full or low speed */
	gusbcfg.b.usbtrdtim = 5;
#endif
	dwc_write_reg32(DWC_REG_GUSBCFG, gusbcfg.d32);

	/* Clear interrupt */
	gintsts.d32 = 0;
	gintsts.b.enumdone = 1;
	dwc_write_reg32(DWC_REG_GINTSTS,gintsts.d32 );
	return 1;
}
///////////////////////////////////////////////////////////////////
/**
 * This interrupt indicates that an OUT EP has a pending Interrupt.
 * The sequence for handling the OUT EP interrupt is shown below:
 * -#	Read the Device All Endpoint Interrupt register
 * -#	Repeat the following for each OUT EP interrupt bit set (from
 *   	LSB to MSB).
 * -#	Read the Device Endpoint Interrupt (DOEPINTn) register
 * -#	If "Transfer Complete" call the request complete function
 * -#	If "Endpoint Disabled" complete the EP disable procedure.
 * -#	If "AHB Error Interrupt" log error
 * -#	If "Setup Phase Done" process Setup Packet (See Standard USB
 *   	Command Processing)
 */
static int32_t dwc_otg_pcd_handle_out_ep_intr(void)
{
#define CLEAR_OUT_EP_INTR(__epnum,__intr) \
do { \
        doepint_data_t doepint = { 0 }; \
	doepint.b.__intr = 1; \
	dwc_write_reg32(DWC_REG_OUT_EP_INTR(__epnum), \
			doepint.d32); \
} while (0)

        uint32_t ep_intr;
        doepint_data_t doepint = { 0 };
        uint32_t epnum = 0;
//	 uint32_t epnum_trans = 0;
        gintsts_data_t gintsts;
        
        DBG( "dwc_otg_pcd_handle_out_ep_intr()\n" );

	/* Read in the device interrupt bits */
        ep_intr = (dwc_read_reg32(DWC_REG_DAINT) &
                dwc_read_reg32( DWC_REG_DAINTMSK));
        ep_intr =( (ep_intr & 0xffff0000) >> 16);

	/* Clear the OUTEPINT in GINTSTS */
	gintsts.d32 = 0;
	gintsts.b.outepintr = 1;
	dwc_write_reg32 (DWC_REG_GINTSTS, gintsts.d32);
	dwc_write_reg32(DWC_REG_DAINT, 0xFFFF0000 );

        while( ep_intr ) {
            if (ep_intr&0x1) {
                    doepint.d32 = (dwc_read_reg32( DWC_REG_OUT_EP_INTR(epnum)) &
            			dwc_read_reg32(DWC_REG_DOEPMSK));
            
                    /* Transfer complete */
			if ( doepint.b.xfercompl ) {
				DBG("EP%d OUT Xfer Complete\n", epnum);

				/* Clear the bit in DOEPINTn for this interrupt */
				CLEAR_OUT_EP_INTR(epnum,xfercompl);

				if (epnum == 0) {
					handle_ep0( 0 );
				} else {
					complete_ep( epnum,0 );
				}
                    }
                    /* Endpoint disable  */
                    if ( doepint.b.epdisabled ) {
                            DBG("EP%d OUT disabled\n", epnum);
				/* Clear the bit in DOEPINTn for this interrupt */
				CLEAR_OUT_EP_INTR(epnum,epdisabled);
                    }
                    /* AHB Error */
                    if ( doepint.b.ahberr ) {
                            DBG("EP%d OUT AHB Error\n", epnum);
				CLEAR_OUT_EP_INTR(epnum,ahberr);
                    }
                    /* Setup Phase Done (contorl EPs) */
                    if ( doepint.b.setup ) {
                            handle_ep0( 0 );
				CLEAR_OUT_EP_INTR(epnum,setup);
                    }
            }
		epnum++;
		ep_intr >>=1;
        }

        return 1;

#undef CLEAR_OUT_EP_INTR
}
/**
 * This interrupt indicates that an IN EP has a pending Interrupt.
 * The sequence for handling the IN EP interrupt is shown below:
 * -#	Read the Device All Endpoint Interrupt register
 * -#	Repeat the following for each IN EP interrupt bit set (from
 *   	LSB to MSB).
 * -#	Read the Device Endpoint Interrupt (DIEPINTn) register
 * -#	If "Transfer Complete" call the request complete function
 * -#	If "Endpoint Disabled" complete the EP disable procedure.
 * -#	If "AHB Error Interrupt" log error
 * -#	If "Time-out Handshake" log error
 * -#	If "IN Token Received when TxFIFO Empty" write packet to Tx
 *   	FIFO.
 * -#	If "IN Token EP Mismatch" (disable, this is handled by EP
 *   	Mismatch Interrupt)
 */
static int32_t dwc_otg_pcd_handle_in_ep_intr(void)
{
#define CLEAR_IN_EP_INTR(__epnum,__intr) \
do { \
        diepint_data_t diepint = { 0 }; \
	diepint.b.__intr = 1; \
	dwc_write_reg32(DWC_REG_IN_EP_INTR(__epnum), \
			diepint.d32); \
} while (0)

        diepint_data_t diepint = { 0 };
//        depctl_data_t diepctl = { 0 };
        uint32_t ep_intr;
        uint32_t epnum = 0;
        gintmsk_data_t intr_mask = {0};
        gintsts_data_t gintsts;

	 DBG( "dwc_otg_pcd_handle_in_ep_intr()\n" );

	/* Read in the device interrupt bits */
        ep_intr = (dwc_read_reg32(DWC_REG_DAINT) &
                dwc_read_reg32( DWC_REG_DAINTMSK));
        ep_intr =( (ep_intr & 0xffff) );


	/* Clear the INEPINT in GINTSTS */
	/* Clear all the interrupt bits for all IN endpoints in DAINT */
	gintsts.d32 = 0;
	gintsts.b.inepint = 1;
	dwc_write_reg32 (DWC_REG_GINTSTS, gintsts.d32);
	dwc_write_reg32(DWC_REG_DAINT, 0xFFFF );
	flush_cpu_cache();

	/* Service the Device IN interrupts for each endpoint */
        while( ep_intr ) {
                if (ep_intr&0x1) {

                        diepint.d32 = (dwc_read_reg32( DWC_REG_IN_EP_INTR(epnum)) &
			            			dwc_read_reg32(DWC_REG_DAINTMSK));       
                        /* Transfer complete */
                        if ( diepint.b.xfercompl ) {

                            
                                /* Disable the NP Tx FIFO Empty
                                 * Interrrupt */
                                intr_mask.b.nptxfempty = 1;
                                dwc_modify_reg32( DWC_REG_GINTMSK, intr_mask.d32, 0);

                                /* Clear the bit in DIEPINTn for this interrupt */
                                CLEAR_IN_EP_INTR(epnum,xfercompl);

                                /* Complete the transfer */
					if (epnum == 0) {
						handle_ep0( 0 );
					} else {
						complete_ep( epnum,1 );
					}
                        }
                        /* Endpoint disable  */
                        if ( diepint.b.epdisabled ) {
                                handle_in_ep_disable_intr( epnum );

                                /* Clear the bit in DIEPINTn for this interrupt */
                                CLEAR_IN_EP_INTR(epnum,epdisabled);
                        }
                        /* AHB Error */
                        if ( diepint.b.ahberr ) {
				/* Clear the bit in DIEPINTn for this interrupt */
				CLEAR_IN_EP_INTR(epnum,ahberr);
                        }
                        /* TimeOUT Handshake (non-ISOC IN EPs) */
                        if ( diepint.b.timeout ) { 
                                handle_in_ep_timeout_intr( epnum );

				CLEAR_IN_EP_INTR(epnum,timeout);
                        }
                        /** IN Token received with TxF Empty */
                        if (diepint.b.intktxfemp){
#if 0
                                if (!ep->stopped && epnum != 0) {
                                        diepmsk_data_t diepmsk = { 0};
                                        diepmsk.b.intktxfemp = 1;
                                        dwc_modify_reg32( &dev_if->dev_global_regs->diepmsk, diepmsk.d32, 0 );       
                                        start_next_request(ep);
                                }
#endif
				CLEAR_IN_EP_INTR(epnum,intktxfemp);
                        }                                        
                        /** IN Token Received with EP mismatch */
                        if (diepint.b.intknepmis){
				CLEAR_IN_EP_INTR(epnum,intknepmis);
                        }
                        /** IN Endpoint NAK Effective */
                        if (diepint.b.inepnakeff){
				CLEAR_IN_EP_INTR(epnum,inepnakeff);
                        }
                }
                epnum++;
                ep_intr >>=1;
        }

        return 1;

#undef CLEAR_IN_EP_INTR
}
/**
 * This function configures EP0 to receive SETUP packets.
 *
 * @todo NGS: Update the comments from the HW FS.
 *
 *  -# Program the following fields in the endpoint specific registers
 *  for Control OUT EP 0, in order to receive a setup packet
 * 	- DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back
 * 	  setup packets)
 *	- DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back
 *	  to back setup packets)
 *      - In DMA mode, DOEPDMA0 Register with a memory address to
 *        store any setup packets received
 *
 */
static void ep0_out_start(void)
{
	deptsiz0_data_t doeptsize0 = { 0};
	depctl_data_t doepctl = { 0 };
	
	doeptsize0.b.supcnt = 3;    
	doeptsize0.b.pktcnt = 1;
	doeptsize0.b.xfersize = 8*3;

	DBG("ep0_out_start()\n");
	dwc_write_reg32( DWC_REG_OUT_EP_TSIZE(0),doeptsize0.d32 );       


	// EP enable
	doepctl.d32 = dwc_read_reg32(DWC_REG_OUT_EP_REG(0));
	doepctl.b.epena = 1;

	doepctl.d32 = 0x80008000;
	dwc_write_reg32(DWC_REG_OUT_EP_REG(0),doepctl.d32);

	flush_cpu_cache();
}
/**
 * This interrupt occurs when a USB Reset is detected.  When the USB
 * Reset Interrupt occurs the device state is set to DEFAULT and the
 * EP0 state is set to IDLE.
 *  -#	Set the NAK bit for all OUT endpoints (DOEPCTLn.SNAK = 1)
 *  -#	Unmask the following interrupt bits
 *  	- DAINTMSK.INEP0 = 1 (Control 0 IN endpoint)
 * 	- DAINTMSK.OUTEP0 = 1 (Control 0 OUT endpoint)
 * 	- DOEPMSK.SETUP = 1
 * 	- DOEPMSK.XferCompl = 1
 * 	- DIEPMSK.XferCompl = 1
 *	- DIEPMSK.TimeOut = 1
 *  -# Program the following fields in the endpoint specific registers
 *  for Control OUT EP 0, in order to receive a setup packet
 * 	- DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back
 * 	  setup packets)
 *	- DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back
 *	  to back setup packets)
 *      - In DMA mode, DOEPDMA0 Register with a memory address to
 *        store any setup packets received
 * At this point, all the required initialization, except for enabling
 * the control 0 OUT endpoint is done, for receiving SETUP packets.
 */
int32_t dwc_otg_pcd_handle_usb_reset_intr(void)
{

        depctl_data_t doepctl = { 0};
        daint_data_t daintmsk = { 0};
        doepmsk_data_t doepmsk = { 0};
        diepmsk_data_t diepmsk = { 0};
        dcfg_data_t dcfg = { 0 };
        depctl_data_t diepctl = { 0};
        depctl_data_t diepctl_rd = { 0};	
        grstctl_t resetctl = { 0 };
        dctl_data_t dctl = { 0 };
        int i = 0;
        gintsts_data_t gintsts;
                        

        DBG("\nUSB RESET\n");


        /* Clear the Remote Wakeup Signalling */
        dctl.b.rmtwkupsig = 1;
        dwc_modify_reg32( DWC_REG_DCTL,dctl.d32, 0 );

        /* Disable all active IN EPs */
        diepctl.b.epdis = 1;
        diepctl.b.snak = 1;
        for (i=0; i < NUM_EP; i++) {
                diepctl_rd.d32 = dwc_read_reg32(DWC_REG_IN_EP_REG(i));
                if (diepctl_rd.b.epena) {
                        dwc_write_reg32(DWC_REG_IN_EP_REG(i),diepctl.d32 );  
                }
        }

        /* Set NAK for all OUT EPs */
        doepctl.b.snak = 1;
        for (i=0; i < NUM_EP; i++) {
                dwc_write_reg32(DWC_REG_OUT_EP_REG(i), doepctl.d32 );
        }

        /* Flush the NP Tx FIFO */
        dwc_otg_flush_tx_fifo( 0 );
        /* Flush the Learning Queue */
        resetctl.b.intknqflsh = 1;
        dwc_write_reg32( DWC_REG_GRSTCTL, resetctl.d32);

        daintmsk.b.inep0 = 1;
        daintmsk.b.outep0 = 1;
        dwc_write_reg32( DWC_REG_DAINTMSK, daintmsk.d32 );

        doepmsk.b.setup = 1;
        doepmsk.b.xfercompl = 1;
        doepmsk.b.ahberr = 1;
        doepmsk.b.epdisabled = 1;
        dwc_write_reg32( DWC_REG_DOEPMSK, doepmsk.d32 );

        diepmsk.b.xfercompl = 1;
        diepmsk.b.timeout = 1;
        diepmsk.b.epdisabled = 1;
        diepmsk.b.ahberr = 1;
        dwc_write_reg32( DWC_REG_DIEPMSK, diepmsk.d32 );       
        /* Reset Device Address */
        dcfg.d32 = dwc_read_reg32( DWC_REG_DCFG);
        dcfg.b.devaddr = 0;
        dwc_write_reg32( DWC_REG_DCFG, dcfg.d32);

        /* setup EP0 to receive SETUP packets */
        ep0_out_start();
        
	/* Clear interrupt */
	gintsts.d32 = 0;
	gintsts.b.usbreset = 1;
	dwc_write_reg32 ( DWC_REG_GINTSTS, gintsts.d32);

	flush_cpu_cache();
        return 1;
}


///////////////////////////////////////////////////////////////////
int dwc_common_irq()
{
	gotgint_data_t gotgint;
	
	gotgint.d32 = dwc_read_reg32(DWC_REG_GOTGINT);
	if(gotgint.d32 == 0)
		return 0;
//	if(gotgint.b.sesreqsucstschng)
//	{
//		DBG("Session Request Success Status Change\n");
//	}
	dwc_write_reg32(DWC_REG_GOTGINT,gotgint.d32); // clear intr


	return 0;
}

int dwc_pcd_irq()
{
	gintsts_data_t  gintr_status;
	gintsts_data_t  gintr_msk;

	gintr_msk.d32 = dwc_read_reg32(DWC_REG_GINTMSK);
	gintr_status.d32 = dwc_read_reg32(DWC_REG_GINTSTS);
	if((gintr_status.d32 & gintr_msk.d32)== 0)
		return 0;
	
	//DBG("irq gintmsk:  0x%08x\n",gintr_msk.d32);
	//DBG("irq gintrsts: 0x%08x\n",gintr_status.d32);

	gintr_status.d32 = gintr_status.d32 & gintr_msk.d32;
	//DBG("irq gintmsk & gintrsts = 0x%08x\n",gintr_status.d32);

	if (gintr_status.b.rxstsqlvl) {
	    dwc_otg_pcd_handle_rx_status_q_level_intr();
	}
	if (gintr_status.b.nptxfempty) {
	    dwc_otg_pcd_handle_np_tx_fifo_empty_intr( );
	}

	if (gintr_status.b.usbreset) {
	   dwc_otg_pcd_handle_usb_reset_intr( );
	}
	if (gintr_status.b.enumdone) {
	    dwc_otg_pcd_handle_enum_done_intr();
	}
	if (gintr_status.b.epmismatch) {
	    //dwc_otg_pcd_handle_ep_mismatch_intr( core_if );
	}
	if (gintr_status.b.inepint) {
	    dwc_otg_pcd_handle_in_ep_intr();
	}
	if (gintr_status.b.outepintr) {
	    dwc_otg_pcd_handle_out_ep_intr( );
	}

	dwc_write_reg32(DWC_REG_GINTSTS,gintr_status.d32);
	flush_cpu_cache();
	return 0;
}


 
