/* ==========================================================================
 * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd.c $
 * $Revision: 1.5 $
 * $Date: 2008-11-27 09:21:25 $
 * $Change: 1115682 $
 *
 * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
 * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
 * otherwise expressly agreed to in writing between Synopsys and you.
 * 
 * The Software IS NOT an item of Licensed Software or Licensed Product under
 * any End User Software License Agreement or Agreement for Licensed Product
 * with Synopsys or any supplement thereto. You are permitted to use and
 * redistribute this Software in source and binary forms, with or without
 * modification, provided that redistributions of source code must retain this
 * notice. You may not view, use, disclose, copy or distribute this file or
 * any information contained herein except pursuant to this license grant from
 * Synopsys. If you do not agree with this notice, including the disclaimer
 * below, then you are not authorized to use the Software.
 * 
 * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * ========================================================================== */
#ifndef DWC_HOST_ONLY

/** @file 
 * This file implements the Peripheral Controller Driver.
 *
 * The Peripheral Controller Driver (PCD) is responsible for
 * translating requests from the Function Driver into the appropriate
 * actions on the DWC_otg controller. It isolates the Function Driver
 * from the specifics of the controller by providing an API to the
 * Function Driver. 
 *
 * The Peripheral Controller Driver for Linux will implement the
 * Gadget API, so that the existing Gadget drivers can be used.
 * (Gadget Driver is the Linux terminology for a Function Driver.)
 * 
 * The Linux Gadget API is defined in the header file
 * <code><linux/usb_gadget.h></code>.  The USB EP operations API is
 * defined in the structure <code>usb_ep_ops</code> and the USB
 * Controller API is defined in the structure
 * <code>usb_gadget_ops</code>.
 *
 * An important function of the PCD is managing interrupts generated
 * by the DWC_otg controller. The implementation of the DWC_otg device
 * mode interrupt service routines is in dwc_otg_pcd_intr.c.
 *
 * @todo Add Device Mode test modes (Test J mode, Test K mode, etc).
 * @todo Does it work when the request size is greater than DEPTSIZ
 * transfer size
 *
 */


#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/string.h>
#include <linux/dma-mapping.h>
#include <linux/version.h>

//#include <asm/arch/lm.h>
//#include <asm/arch/irqs.h>
#include <asm/rt2880/lm.h>

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
# include <linux/usb/ch9.h>
#else
# include <linux/usb_ch9.h>
#endif

#include <linux/usb_gadget.h>

	
	
#include "dwc_otg_driver.h"
#include "dwc_otg_pcd.h"



/**
 * Static PCD pointer for use in usb_gadget_register_driver and
 * usb_gadget_unregister_driver.  Initialized in dwc_otg_pcd_init.
 */
static	 dwc_otg_pcd_t *s_pcd = 0;


/* Display the contents of the buffer */
extern void dump_msg(const u8 *buf, unsigned int length);


/**
 * This function completes a request.  It call's the request call back.
 */
void dwc_otg_request_done(dwc_otg_pcd_ep_t *ep, dwc_otg_pcd_request_t *req, 
				  int status)
{
	unsigned stopped = ep->stopped;

	DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, ep);
	list_del_init(&req->queue);

	if (req->req.status == -EINPROGRESS) {
		req->req.status = status;
	} else {	
		status = req->req.status;
	}
		
	/* don't modify queue heads during completion callback */
	ep->stopped = 1;
	SPIN_UNLOCK(&ep->pcd->lock);
	req->req.complete(&ep->ep, &req->req);
	SPIN_LOCK(&ep->pcd->lock);

	if (ep->pcd->request_pending > 0) {
		--ep->pcd->request_pending;
	}
		
	ep->stopped = stopped;
}

/**
 * This function terminates all the requsts in the EP request queue.
 */
void dwc_otg_request_nuke(dwc_otg_pcd_ep_t *ep)
{
	dwc_otg_pcd_request_t *req;

	ep->stopped = 1;

	/* called with irqs blocked?? */
	while (!list_empty(&ep->queue)) {
		req = list_entry(ep->queue.next, dwc_otg_pcd_request_t,
				 queue);
		dwc_otg_request_done(ep, req, -ESHUTDOWN);
	}
}

/* USB Endpoint Operations */
/* 
 * The following sections briefly describe the behavior of the Gadget
 * API endpoint operations implemented in the DWC_otg driver
 * software. Detailed descriptions of the generic behavior of each of
 * these functions can be found in the Linux header file
 * include/linux/usb_gadget.h.
 *
 * The Gadget API provides wrapper functions for each of the function
 * pointers defined in usb_ep_ops. The Gadget Driver calls the wrapper
 * function, which then calls the underlying PCD function. The
 * following sections are named according to the wrapper
 * functions. Within each section, the corresponding DWC_otg PCD
 * function name is specified.
 *
 */

/**
 * This function assigns periodic Tx FIFO to an periodic EP
 * in shared Tx FIFO mode
 */
static uint32_t assign_perio_tx_fifo(dwc_otg_core_if_t	*core_if)
{
	uint32_t PerTxMsk = 1;
	int i;
	for(i = 0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; ++i)
	{
		if((PerTxMsk & core_if->p_tx_msk) == 0) {
			core_if->p_tx_msk |= PerTxMsk;
			return i + 1;
		}
		PerTxMsk <<= 1;
	}
	return 0;
}
/**
 * This function releases periodic Tx FIFO 
 * in shared Tx FIFO mode
 */
static void release_perio_tx_fifo(dwc_otg_core_if_t *core_if, uint32_t fifo_num)
{
	core_if->p_tx_msk = (core_if->p_tx_msk & (1 << (fifo_num - 1))) ^ core_if->p_tx_msk;
}
/**
 * This function assigns periodic Tx FIFO to an periodic EP
 * in shared Tx FIFO mode
 */
static uint32_t assign_tx_fifo(dwc_otg_core_if_t *core_if)
{
	uint32_t TxMsk = 1;
	int i;
	
	for(i = 0; i < core_if->hwcfg4.b.num_in_eps; ++i)
	{
		if((TxMsk & core_if->tx_msk) == 0) {
			core_if->tx_msk |= TxMsk;
			return i + 1;
		}
		TxMsk <<= 1;
	}
	return 0;
}
/**
 * This function releases periodic Tx FIFO 
 * in shared Tx FIFO mode
 */
static void release_tx_fifo(dwc_otg_core_if_t	*core_if, uint32_t fifo_num)
{
	core_if->tx_msk = (core_if->tx_msk & (1 << (fifo_num - 1))) ^ core_if->tx_msk;
}

/**
 * This function is called by the Gadget Driver for each EP to be
 * configured for the current configuration (SET_CONFIGURATION).  
 * 
 * This function initializes the dwc_otg_ep_t data structure, and then
 * calls dwc_otg_ep_activate.
 */
static int dwc_otg_pcd_ep_enable(struct usb_ep *usb_ep, 
				 const struct usb_endpoint_descriptor *ep_desc)
{
	dwc_otg_pcd_ep_t *ep = 0;
	dwc_otg_pcd_t *pcd = 0;
	unsigned long flags;
	
	DWC_DEBUGPL(DBG_PCDV,"%s(%p,%p)\n", __func__, usb_ep, ep_desc);

	ep = container_of(usb_ep, dwc_otg_pcd_ep_t, ep);
	if (!usb_ep || !ep_desc || ep->desc || 
			ep_desc->bDescriptorType != USB_DT_ENDPOINT) {
		DWC_WARN("%s, bad ep or descriptor\n", __func__);
		return -EINVAL;
	}
	if (ep == &ep->pcd->ep0) {
		DWC_WARN("%s, bad ep(0)\n", __func__);
		return -EINVAL;
	}
		
	/* Check FIFO size? */
	if (!ep_desc->wMaxPacketSize) {
		DWC_WARN("%s, bad %s maxpacket\n", __func__, usb_ep->name);
		return -ERANGE;
	}

	pcd = ep->pcd;
	if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) {
		DWC_WARN("%s, bogus device state\n", __func__);
		return -ESHUTDOWN;
	}

	SPIN_LOCK_IRQSAVE(&pcd->lock, flags);
		
	ep->desc = ep_desc;
	ep->ep.maxpacket = le16_to_cpu (ep_desc->wMaxPacketSize);
		
	/*
	 * Activate the EP
	 */
	ep->stopped = 0;
		
	ep->dwc_ep.is_in = (USB_DIR_IN & ep_desc->bEndpointAddress) != 0;
	ep->dwc_ep.maxpacket = ep->ep.maxpacket;
	
	ep->dwc_ep.type = ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;

	if(ep->dwc_ep.is_in) {
		if(!pcd->otg_dev->core_if->en_multiple_tx_fifo) {
			ep->dwc_ep.tx_fifo_num = 0;
		
			if (ep->dwc_ep.type == USB_ENDPOINT_XFER_ISOC) {
				/* 
				 * if ISOC EP then assign a Periodic Tx FIFO.
				 */
				ep->dwc_ep.tx_fifo_num = assign_perio_tx_fifo(pcd->otg_dev->core_if);
			 }
		} else {
			/* 
			 * if Dedicated FIFOs mode is on then assign a Tx FIFO.
			 */
			ep->dwc_ep.tx_fifo_num = assign_tx_fifo(pcd->otg_dev->core_if);

		}
	}		 
	/* Set initial data PID. */
	if (ep->dwc_ep.type == USB_ENDPOINT_XFER_BULK) {
		ep->dwc_ep.data_pid_start = 0;	
	}
	
	DWC_DEBUGPL(DBG_PCD, "Activate %s-%s: type=%d, mps=%d desc=%p\n", 
					ep->ep.name, (ep->dwc_ep.is_in ?"IN":"OUT"),
					ep->dwc_ep.type, ep->dwc_ep.maxpacket, ep->desc);
		
	if(ep->dwc_ep.type != USB_ENDPOINT_XFER_ISOC) {
		ep->dwc_ep.desc_addr = dwc_otg_ep_alloc_desc_chain(&ep->dwc_ep.dma_desc_addr, MAX_DMA_DESC_CNT);
	}

	dwc_otg_ep_activate(GET_CORE_IF(pcd), &ep->dwc_ep);
	SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags);

	return 0;
}

/** 
 * This function is called when an EP is disabled due to disconnect or
 * change in configuration. Any pending requests will terminate with a
 * status of -ESHUTDOWN.
 *
 * This function modifies the dwc_otg_ep_t data structure for this EP,
 * and then calls dwc_otg_ep_deactivate.
 */
static int dwc_otg_pcd_ep_disable(struct usb_ep *usb_ep)
{
	dwc_otg_pcd_ep_t *ep;
	dwc_otg_pcd_t *pcd = 0;
	unsigned long flags;

	DWC_DEBUGPL(DBG_PCDV,"%s(%p)\n", __func__, usb_ep);
	ep = container_of(usb_ep, dwc_otg_pcd_ep_t, ep);
	if (!usb_ep || !ep->desc) {
		DWC_DEBUGPL(DBG_PCD, "%s, %s not enabled\n", __func__,
			usb_ep ? ep->ep.name : NULL);
		return -EINVAL;
	}
		
	SPIN_LOCK_IRQSAVE(&ep->pcd->lock, flags);

	dwc_otg_request_nuke(ep);		   

	dwc_otg_ep_deactivate(GET_CORE_IF(ep->pcd), &ep->dwc_ep);
	ep->desc = 0;
	ep->stopped = 1;
	
	if(ep->dwc_ep.is_in) {
		dwc_otg_flush_tx_fifo(GET_CORE_IF(ep->pcd), ep->dwc_ep.tx_fifo_num);
		release_perio_tx_fifo(GET_CORE_IF(ep->pcd), ep->dwc_ep.tx_fifo_num);
		release_tx_fifo(GET_CORE_IF(ep->pcd), ep->dwc_ep.tx_fifo_num);
	}	
	
	/* Free DMA Descriptors */
	pcd = ep->pcd;

	SPIN_UNLOCK_IRQRESTORE(&ep->pcd->lock, flags);

	if(ep->dwc_ep.type != USB_ENDPOINT_XFER_ISOC && ep->dwc_ep.desc_addr) {
		dwc_otg_ep_free_desc_chain(ep->dwc_ep.desc_addr, ep->dwc_ep.dma_desc_addr, MAX_DMA_DESC_CNT);
	}

	DWC_DEBUGPL(DBG_PCD, "%s disabled\n", usb_ep->name);
	return 0;
}


/**
 * This function allocates a request object to use with the specified
 * endpoint.
 *
 * @param ep The endpoint to be used with with the request
 * @param gfp_flags the GFP_* flags to use.
 */
static struct usb_request *dwc_otg_pcd_alloc_request(struct usb_ep *ep,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
						     int gfp_flags
#else
						     gfp_t gfp_flags
#endif
						   )
{
	dwc_otg_pcd_request_t *req;
	
	DWC_DEBUGPL(DBG_PCDV,"%s(%p,%d)\n", __func__, ep, gfp_flags);
	if (0 == ep) {
		DWC_WARN("%s() %s\n", __func__, "Invalid EP!\n");
		return 0;
	}
	req = kmalloc(sizeof(dwc_otg_pcd_request_t), gfp_flags);
	if (0 == req) {
		DWC_WARN("%s() %s\n", __func__, 
				 "request allocation failed!\n");
		return 0;
	}
	memset(req, 0, sizeof(dwc_otg_pcd_request_t));
	req->req.dma = DMA_ADDR_INVALID;
	INIT_LIST_HEAD(&req->queue);
	return &req->req;
}

/**
 * This function frees a request object.
 *
 * @param ep The endpoint associated with the request
 * @param req The request being freed
 */
static void dwc_otg_pcd_free_request(struct usb_ep *ep,
					 struct usb_request *req)
{
	dwc_otg_pcd_request_t *request;
	DWC_DEBUGPL(DBG_PCDV,"%s(%p,%p)\n", __func__, ep, req);

	if (0 == ep || 0 == req) {
		DWC_WARN("%s() %s\n", __func__, 
				 "Invalid ep or req argument!\n");
		return;
	}
		
	request = container_of(req, dwc_otg_pcd_request_t, req);
	kfree(request);
}

/**
 * This function allocates an I/O buffer to be used for a transfer
 * to/from the specified endpoint.
 * 
 * @param usb_ep The endpoint to be used with with the request
 * @param bytes The desired number of bytes for the buffer
 * @param dma Pointer to the buffer's DMA address; must be valid
 * @param gfp_flags the GFP_* flags to use.
 * @return address of a new buffer or null is buffer could not be allocated.
 */
static void *dwc_otg_pcd_alloc_buffer(struct usb_ep *usb_ep, unsigned bytes,
				      dma_addr_t *dma,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
				      int gfp_flags
#else
				      gfp_t gfp_flags
#endif
				    )
{
	void *buf;
	dwc_otg_pcd_ep_t *ep;
	dwc_otg_pcd_t *pcd = 0;

	ep = container_of(usb_ep, dwc_otg_pcd_ep_t, ep);
	pcd = ep->pcd;

	DWC_DEBUGPL(DBG_PCDV,"%s(%p,%d,%p,%0x)\n", __func__, usb_ep, bytes, 
				dma, gfp_flags);

	/* Check dword alignment */
	if ((bytes & 0x3UL) != 0) {
		DWC_WARN("%s() Buffer size is not a multiple of" 
				 "DWORD size (%d)",__func__, bytes);
	}

	if (GET_CORE_IF(pcd)->dma_enable) {
		buf = dma_alloc_coherent (NULL, bytes, dma, gfp_flags);
	}
	else {
		buf = kmalloc(bytes, gfp_flags);
	}

	/* Check dword alignment */
	if (((int)buf & 0x3UL) != 0) {
		DWC_WARN("%s() Buffer is not DWORD aligned (%p)",
					__func__, buf);
	}
		
	return buf;
}

/**
 * This function frees an I/O buffer that was allocated by alloc_buffer.
 *
 * @param usb_ep the endpoint associated with the buffer
 * @param buf address of the buffer
 * @param dma The buffer's DMA address
 * @param bytes The number of bytes of the buffer
 */
static void dwc_otg_pcd_free_buffer(struct usb_ep *usb_ep, void *buf,
					dma_addr_t dma, unsigned bytes)
{
	dwc_otg_pcd_ep_t *ep;
	dwc_otg_pcd_t *pcd = 0;

	ep = container_of(usb_ep, dwc_otg_pcd_ep_t, ep);
	pcd = ep->pcd;

	DWC_DEBUGPL(DBG_PCDV,"%s(%p,%p,%0x,%d)\n", __func__, ep, buf, dma, bytes);
	
	if (GET_CORE_IF(pcd)->dma_enable) {
		dma_free_coherent (NULL, bytes, buf, dma);
	}
	else {
		kfree(buf);
	}
}


/**
 * This function is used to submit an I/O Request to an EP.
 *
 *	- When the request completes the request's completion callback
 *	  is called to return the request to the driver.
 *	- An EP, except control EPs, may have multiple requests
 *	  pending.
 *	- Once submitted the request cannot be examined or modified.
 *	- Each request is turned into one or more packets.
 *	- A BULK EP can queue any amount of data; the transfer is
 *	  packetized.
 *	- Zero length Packets are specified with the request 'zero'
 *	  flag.
 */
static int dwc_otg_pcd_ep_queue(struct usb_ep *usb_ep, 
				struct usb_request *usb_req,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
				int gfp_flags
#else
				gfp_t gfp_flags
#endif
			      )
{
	int prevented = 0;
	dwc_otg_pcd_request_t *req;
	dwc_otg_pcd_ep_t *ep;
	dwc_otg_pcd_t	*pcd;
	unsigned long flags = 0;
	dwc_otg_core_if_t *_core_if;

	DWC_DEBUGPL(DBG_PCDV,"%s(%p,%p,%d)\n", 
			__func__, usb_ep, usb_req, gfp_flags);
		
	req = container_of(usb_req, dwc_otg_pcd_request_t, req);
	if (!usb_req || !usb_req->complete || !usb_req->buf || 
			!list_empty(&req->queue)) {
		DWC_WARN("%s, bad params\n", __func__);
		return -EINVAL;
	}
		
	ep = container_of(usb_ep, dwc_otg_pcd_ep_t, ep);
	if (!usb_ep || (!ep->desc && ep->dwc_ep.num != 0)/* || ep->stopped != 0*/) {
		DWC_WARN("%s, bad ep\n", __func__);
		return -EINVAL;
	}

	pcd = ep->pcd;
	if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) {
		DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n", pcd->gadget.speed);
		DWC_WARN("%s, bogus device state\n", __func__);
		return -ESHUTDOWN;
	}


	DWC_DEBUGPL(DBG_PCD, "%s queue req %p, len %d buf %p\n",
			   usb_ep->name, usb_req, usb_req->length, usb_req->buf);

	if (!GET_CORE_IF(pcd)->core_params->opt) {
		if (ep->dwc_ep.num != 0) {
			DWC_ERROR("%s queue req %p, len %d buf %p\n",
					  usb_ep->name, usb_req, usb_req->length, usb_req->buf);
		}
	}

	SPIN_LOCK_IRQSAVE(&ep->pcd->lock, flags);

	
	/**************************************************
         New add by kaiker ,for DMA mode bug
	************************************************/
	//by kaiker ,for RT3052 USB OTG device mode

	_core_if = GET_CORE_IF(pcd);

	if (_core_if->dma_enable) 
	{
		 usb_req->dma = virt_to_phys((void *)usb_req->buf);
		  
		if(ep->dwc_ep.is_in)
		{
			if(usb_req->length)
				dma_cache_wback_inv((unsigned long)usb_req->buf, usb_req->length + 2);
		}
	}

	

#if defined(DEBUG) & defined(VERBOSE)
	dump_msg(usb_req->buf, usb_req->length);
#endif	

	usb_req->status = -EINPROGRESS;
	usb_req->actual = 0;

	/* 
	 * For EP0 IN without premature status, zlp is required?
	 */
	if (ep->dwc_ep.num == 0 && ep->dwc_ep.is_in) {
		DWC_DEBUGPL(DBG_PCDV, "%s-OUT ZLP\n", usb_ep->name);
		//_req->zero = 1;
	}

	/* Start the transfer */
	if (list_empty(&ep->queue) && !ep->stopped) {
		/* EP0 Transfer? */
		if (ep->dwc_ep.num == 0) {
			switch (pcd->ep0state) {
			case EP0_IN_DATA_PHASE:
				DWC_DEBUGPL(DBG_PCD, 
						"%s ep0: EP0_IN_DATA_PHASE\n", 
						__func__);
				break;

			case EP0_OUT_DATA_PHASE:
				DWC_DEBUGPL(DBG_PCD, 
						"%s ep0: EP0_OUT_DATA_PHASE\n", 
						__func__);
				if (pcd->request_config) { 
					/* Complete STATUS PHASE */
					ep->dwc_ep.is_in = 1;
					pcd->ep0state = EP0_IN_STATUS_PHASE;
				}
				break;
						
			case EP0_IN_STATUS_PHASE:
				DWC_DEBUGPL(DBG_PCD, 
						"%s ep0: EP0_IN_STATUS_PHASE\n", 
						__func__);
				break;

			default:
				DWC_DEBUGPL(DBG_ANY, "ep0: odd state %d\n", 
						pcd->ep0state);
				SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags);
				return -EL2HLT;
			}
			ep->dwc_ep.dma_addr = usb_req->dma;
			ep->dwc_ep.start_xfer_buff = usb_req->buf;
			ep->dwc_ep.xfer_buff = usb_req->buf;
			ep->dwc_ep.xfer_len = usb_req->length;
			ep->dwc_ep.xfer_count = 0;
			ep->dwc_ep.sent_zlp = 0;
			ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
			
			if(usb_req->zero) {
				if((ep->dwc_ep.xfer_len % ep->dwc_ep.maxpacket == 0) 
						&& (ep->dwc_ep.xfer_len != 0)) {
					ep->dwc_ep.sent_zlp = 1;
				}
				
			}

			dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep->dwc_ep);
		} 
		else {
			
			uint32_t max_transfer = GET_CORE_IF(ep->pcd)->core_params->max_transfer_size;
			
			/* Setup and start the Transfer */
			ep->dwc_ep.dma_addr = usb_req->dma;
			ep->dwc_ep.start_xfer_buff = usb_req->buf;
			ep->dwc_ep.xfer_buff = usb_req->buf;
			ep->dwc_ep.sent_zlp = 0;
			ep->dwc_ep.total_len = usb_req->length;
			ep->dwc_ep.xfer_len = 0;
			ep->dwc_ep.xfer_count = 0;
			
			if(max_transfer > MAX_TRANSFER_SIZE) {
				ep->dwc_ep.maxxfer = max_transfer - (max_transfer % ep->dwc_ep.maxpacket);
			} else {
				ep->dwc_ep.maxxfer = max_transfer;
			}

			if(usb_req->zero) {
				if((ep->dwc_ep.total_len % ep->dwc_ep.maxpacket == 0) 
						&& (ep->dwc_ep.total_len != 0)) {
					ep->dwc_ep.sent_zlp = 1;
				}
				
			}
			dwc_otg_ep_start_transfer(GET_CORE_IF(pcd), &ep->dwc_ep);
		}
	}

	if ((req != 0) || prevented) {
		++pcd->request_pending;
		list_add_tail(&req->queue, &ep->queue);
		if (ep->dwc_ep.is_in && ep->stopped && !(GET_CORE_IF(pcd)->dma_enable)) {
			/** @todo NGS Create a function for this. */
			diepmsk_data_t diepmsk = { .d32 = 0};
			diepmsk.b.intktxfemp = 1;
			if(&GET_CORE_IF(pcd)->multiproc_int_enable) {
				dwc_modify_reg32(&GET_CORE_IF(pcd)->dev_if->dev_global_regs->diepeachintmsk[ep->dwc_ep.num],
							0, diepmsk.d32);
			} else {
				dwc_modify_reg32(&GET_CORE_IF(pcd)->dev_if->dev_global_regs->diepmsk, 0, diepmsk.d32);
			}
		}
	}
		
	SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags);
	return 0;
}

/**
 * This function cancels an I/O request from an EP.
 */
static int dwc_otg_pcd_ep_dequeue(struct usb_ep *usb_ep,
				  struct usb_request *usb_req)
{
	dwc_otg_pcd_request_t *req;
	dwc_otg_pcd_ep_t *ep;
	dwc_otg_pcd_t	*pcd;
	unsigned long flags;

	DWC_DEBUGPL(DBG_PCDV,"%s(%p,%p)\n", __func__, usb_ep, usb_req);
		
	ep = container_of(usb_ep, dwc_otg_pcd_ep_t, ep);
	if (!usb_ep || !usb_req || (!ep->desc && ep->dwc_ep.num != 0)) {
		DWC_WARN("%s, bad argument\n", __func__);
		return -EINVAL;
	}
	pcd = ep->pcd;
	if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) {
		DWC_WARN("%s, bogus device state\n", __func__);
		return -ESHUTDOWN;
	}

	SPIN_LOCK_IRQSAVE(&pcd->lock, flags);
	DWC_DEBUGPL(DBG_PCDV, "%s %s %s %p\n", __func__, usb_ep->name,
					ep->dwc_ep.is_in ? "IN" : "OUT",
					usb_req);

	/* make sure it's actually queued on this endpoint */
	list_for_each_entry(req, &ep->queue, queue) 
	{
		if (&req->req == usb_req) {
			break;
		}
	}

	if (&req->req != usb_req) {
		SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags);
		return -EINVAL;
	}

	if (!list_empty(&req->queue)) {
		dwc_otg_request_done(ep, req, -ECONNRESET);
	} 
	else {
		req = 0;
	}
		
	SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags);

	return req ? 0 : -EOPNOTSUPP;
}

/**
 * usb_ep_set_halt stalls an endpoint. 
 *
 * usb_ep_clear_halt clears an endpoint halt and resets its data
 * toggle.
 *
 * Both of these functions are implemented with the same underlying
 * function. The behavior depends on the value argument.
 * 
 * @param[in] usb_ep the Endpoint to halt or clear halt.
 * @param[in] value 
 *	- 0 means clear_halt.
 *	- 1 means set_halt, 
 *	- 2 means clear stall lock flag.
 *	- 3 means set  stall lock flag.
 */
static int dwc_otg_pcd_ep_set_halt(struct usb_ep *usb_ep, int value)
{
	int retval = 0;
	unsigned long flags;
	dwc_otg_pcd_ep_t *ep = 0;
		
		
	DWC_DEBUGPL(DBG_PCD,"HALT %s %d\n", usb_ep->name, value);

	ep = container_of(usb_ep, dwc_otg_pcd_ep_t, ep);

	if (!usb_ep || (!ep->desc && ep != &ep->pcd->ep0) ||
			ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
		DWC_WARN("%s, bad ep\n", __func__);
		return -EINVAL;
	}
		
	SPIN_LOCK_IRQSAVE(&ep->pcd->lock, flags);
	if (!list_empty(&ep->queue)) {
		DWC_WARN("%s() %s XFer In process\n", __func__, usb_ep->name);
		retval = -EAGAIN;
	}
	else if (value == 0) {
		dwc_otg_ep_clear_stall(ep->pcd->otg_dev->core_if, 
					&ep->dwc_ep);		 
	}
	else if(value == 1) {
		if (ep->dwc_ep.is_in == 1 && ep->pcd->otg_dev->core_if->dma_desc_enable) {
			dtxfsts_data_t txstatus;
			fifosize_data_t txfifosize;

			txfifosize.d32 = dwc_read_reg32(&ep->pcd->otg_dev->core_if->core_global_regs->dptxfsiz_dieptxf[ep->dwc_ep.tx_fifo_num]);
			txstatus.d32 = dwc_read_reg32(&ep->pcd->otg_dev->core_if->dev_if->in_ep_regs[ep->dwc_ep.num]->dtxfsts);

			if(txstatus.b.txfspcavail < txfifosize.b.depth) {
				DWC_WARN("%s() %s Data In Tx Fifo\n", __func__, usb_ep->name);
				retval = -EAGAIN;
			}
			else {
				if (ep->dwc_ep.num == 0) {
					ep->pcd->ep0state = EP0_STALL;
				}
				
				ep->stopped = 1;
				dwc_otg_ep_set_stall(ep->pcd->otg_dev->core_if, 
							&ep->dwc_ep);
			}
		}
		else {
			if (ep->dwc_ep.num == 0) {
				ep->pcd->ep0state = EP0_STALL;
			}
			
			ep->stopped = 1;
			dwc_otg_ep_set_stall(ep->pcd->otg_dev->core_if, 
						&ep->dwc_ep);
		}
	}
	else if (value == 2) {
		ep->dwc_ep.stall_clear_flag = 0;
	}
	else if (value == 3) {
		ep->dwc_ep.stall_clear_flag = 1;
	}
	
	SPIN_UNLOCK_IRQRESTORE(&ep->pcd->lock, flags);
	return retval;
}

/**
 * This function allocates a DMA Descriptor chain for the Endpoint 
 * buffer to be used for a transfer to/from the specified endpoint.
 */
dwc_otg_dma_desc_t* dwc_otg_ep_alloc_desc_chain(uint32_t * dma_desc_addr, uint32_t count)
{

	return dma_alloc_coherent(NULL, count * sizeof(dwc_otg_dma_desc_t), dma_desc_addr, GFP_KERNEL);
}

/**
 * This function frees a DMA Descriptor chain that was allocated by ep_alloc_desc.
 */
void dwc_otg_ep_free_desc_chain(dwc_otg_dma_desc_t* desc_addr, uint32_t dma_desc_addr, uint32_t count)
{
	dma_free_coherent(NULL, count * sizeof(dwc_otg_dma_desc_t), desc_addr, dma_desc_addr);
}

#ifdef DWC_EN_ISOC

/**
 * This function initializes a descriptor chain for Isochronous transfer
 *
 * @param core_if Programming view of DWC_otg controller.
 * @param dwc_ep The EP to start the transfer on.
 *
 */
void dwc_otg_iso_ep_start_ddma_transfer(dwc_otg_core_if_t *core_if, dwc_ep_t *dwc_ep)
{
	
 	dsts_data_t 		dsts = { .d32 = 0};
	depctl_data_t 		depctl = { .d32 = 0 };
	volatile uint32_t 	*addr;
 	int 			i, j;
		
	if(dwc_ep->is_in) 
		dwc_ep->desc_cnt = dwc_ep->buf_proc_intrvl / dwc_ep->bInterval;
	else
		dwc_ep->desc_cnt = dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm / dwc_ep->bInterval;
	
	
	/** Allocate descriptors for double buffering */
	dwc_ep->iso_desc_addr = dwc_otg_ep_alloc_desc_chain(&dwc_ep->iso_dma_desc_addr,dwc_ep->desc_cnt*2);
	if(dwc_ep->desc_addr) {
		DWC_WARN("%s, can't allocate DMA descriptor chain\n", __func__);
		return;
	}

	dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts);

	/** ISO OUT EP */
	if(dwc_ep->is_in == 0) {
		desc_sts_data_t sts = { .d32 =0 };
		dwc_otg_dma_desc_t* dma_desc = dwc_ep->iso_desc_addr;
		dma_addr_t dma_ad;
		uint32_t data_per_desc;
		dwc_otg_dev_out_ep_regs_t *out_regs = 
			core_if->dev_if->out_ep_regs[dwc_ep->num];
		int	offset;

		addr = &core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl;
		dma_ad = (dma_addr_t)dwc_read_reg32(&(out_regs->doepdma));

		/** Buffer 0 descriptors setup */
		dma_ad = dwc_ep->dma_addr0;

		sts.b_iso_out.bs = BS_HOST_READY;
		sts.b_iso_out.rxsts = 0;
		sts.b_iso_out.l = 0;
		sts.b_iso_out.sp = 0; 
		sts.b_iso_out.ioc = 0;
		sts.b_iso_out.pid = 0;
		sts.b_iso_out.framenum = 0;

		offset = 0;
		for(i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; i+= dwc_ep->pkt_per_frm)
		{
			
			for(j = 0; j < dwc_ep->pkt_per_frm; ++j)
			{
				data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ?
					dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket;
				
				data_per_desc += (data_per_desc % 4) ? (4 - data_per_desc % 4):0;
				sts.b_iso_out.rxbytes = data_per_desc;
				writel((uint32_t)dma_ad, &dma_desc->buf);
				writel(sts.d32, &dma_desc->status); 

				offset += data_per_desc;
				dma_desc ++;			
				(uint32_t)dma_ad += data_per_desc;
			}
		}

		for(j = 0; j < dwc_ep->pkt_per_frm - 1; ++j)
		{
			data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ?
				dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket;
			data_per_desc += (data_per_desc % 4) ? (4 - data_per_desc % 4):0;
			sts.b_iso_out.rxbytes = data_per_desc;
			writel((uint32_t)dma_ad, &dma_desc->buf);
			writel(sts.d32, &dma_desc->status); 

			offset += data_per_desc;
			dma_desc ++;		
			(uint32_t)dma_ad += data_per_desc;
		}

		sts.b_iso_out.ioc = 1;
		data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ?
			dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket;
		data_per_desc += (data_per_desc % 4) ? (4 - data_per_desc % 4):0;
		sts.b_iso_out.rxbytes = data_per_desc;

		writel((uint32_t)dma_ad, &dma_desc->buf);
		writel(sts.d32, &dma_desc->status); 
		dma_desc ++;	
		
		/** Buffer 1 descriptors setup */
		sts.b_iso_out.ioc = 0;
		dma_ad = dwc_ep->dma_addr1;

		offset = 0;
		for(i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; i+= dwc_ep->pkt_per_frm)
		{
			for(j = 0; j < dwc_ep->pkt_per_frm; ++j)
			{
				data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ?
					dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket;
				data_per_desc += (data_per_desc % 4) ? (4 - data_per_desc % 4):0;
				sts.b_iso_out.rxbytes = data_per_desc;
				writel((uint32_t)dma_ad, &dma_desc->buf);
				writel(sts.d32, &dma_desc->status);
				
				offset += data_per_desc;
				dma_desc ++;			
				(uint32_t)dma_ad += data_per_desc;
			}
		}
		for(j = 0; j < dwc_ep->pkt_per_frm - 1; ++j)
		{
			data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ?
				dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket;
			data_per_desc += (data_per_desc % 4) ? (4 - data_per_desc % 4):0;
			sts.b_iso_out.rxbytes = data_per_desc;
			writel((uint32_t)dma_ad, &dma_desc->buf);
			writel(sts.d32, &dma_desc->status); 
			
			offset += data_per_desc;
			dma_desc ++;		
			(uint32_t)dma_ad += data_per_desc;
		}

		sts.b_iso_out.ioc = 1;
		sts.b_iso_out.l = 1;
		data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ?
			dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket;
		data_per_desc += (data_per_desc % 4) ? (4 - data_per_desc % 4):0;
		sts.b_iso_out.rxbytes = data_per_desc;

		writel((uint32_t)dma_ad, &dma_desc->buf);
		writel(sts.d32, &dma_desc->status); 

		dwc_ep->next_frame = 0;
			
		/** Write dma_ad into DOEPDMA register */
		dwc_write_reg32(&(out_regs->doepdma),(uint32_t)dwc_ep->iso_dma_desc_addr);

	}
	/** ISO IN EP */
	else {
		desc_sts_data_t sts = { .d32 =0 };
		dwc_otg_dma_desc_t* dma_desc = dwc_ep->iso_desc_addr;
		dma_addr_t dma_ad;
		dwc_otg_dev_in_ep_regs_t *in_regs = 
			core_if->dev_if->in_ep_regs[dwc_ep->num];
		unsigned int		   frmnumber;
		fifosize_data_t		txfifosize,rxfifosize;

		txfifosize.d32 = dwc_read_reg32(&core_if->dev_if->in_ep_regs[dwc_ep->num]->dtxfsts);
		rxfifosize.d32 = dwc_read_reg32(&core_if->core_global_regs->grxfsiz);

		
		addr = &core_if->dev_if->in_ep_regs[dwc_ep->num]->diepctl;

		dma_ad = dwc_ep->dma_addr0;
		
		dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts);

		sts.b_iso_in.bs = BS_HOST_READY;
		sts.b_iso_in.txsts = 0;
		sts.b_iso_in.sp = (dwc_ep->data_per_frame % dwc_ep->maxpacket)? 1 : 0;
		sts.b_iso_in.ioc = 0;
		sts.b_iso_in.pid = dwc_ep->pkt_per_frm;

		
		frmnumber = dwc_ep->next_frame;

		sts.b_iso_in.framenum = frmnumber;
		sts.b_iso_in.txbytes = dwc_ep->data_per_frame;
		sts.b_iso_in.l = 0;
		
		/** Buffer 0 descriptors setup */
		for(i = 0; i < dwc_ep->desc_cnt - 1; i++)
		{
			writel((uint32_t)dma_ad, &dma_desc->buf);
			writel(sts.d32, &dma_desc->status); 
			dma_desc ++;				
			
			(uint32_t)dma_ad += dwc_ep->data_per_frame;
			sts.b_iso_in.framenum += dwc_ep->bInterval;			
		}
		
		sts.b_iso_in.ioc = 1;
		writel((uint32_t)dma_ad, &dma_desc->buf);
		writel(sts.d32, &dma_desc->status); 
		++dma_desc;

		/** Buffer 1 descriptors setup */
		sts.b_iso_in.ioc = 0;
		dma_ad = dwc_ep->dma_addr1;

		for(i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; i+= dwc_ep->pkt_per_frm)
		{
			writel((uint32_t)dma_ad, &dma_desc->buf);
			writel(sts.d32, &dma_desc->status); 
			dma_desc ++;				
			
			(uint32_t)dma_ad += dwc_ep->data_per_frame;
			sts.b_iso_in.framenum += dwc_ep->bInterval;

			sts.b_iso_in.ioc = 0;
		}
		sts.b_iso_in.ioc = 1;
		sts.b_iso_in.l = 1;

		writel((uint32_t)dma_ad, &dma_desc->buf);
		writel(sts.d32, &dma_desc->status); 
		
		dwc_ep->next_frame = sts.b_iso_in.framenum + dwc_ep->bInterval;

		/** Write dma_ad into diepdma register */
		dwc_write_reg32(&(in_regs->diepdma),(uint32_t)dwc_ep->iso_dma_desc_addr);
	}
	/** Enable endpoint, clear nak  */
	depctl.d32 = 0;	
	depctl.b.epena = 1;
	depctl.b.usbactep = 1;
	depctl.b.cnak = 1;
	
	dwc_modify_reg32(addr, depctl.d32,depctl.d32);
	depctl.d32 = dwc_read_reg32(addr);
}

/**
 * This function initializes a descriptor chain for Isochronous transfer
 *
 * @param core_if Programming view of DWC_otg controller.
 * @param ep The EP to start the transfer on.
 *
 */

void dwc_otg_iso_ep_start_buf_transfer(dwc_otg_core_if_t *core_if, dwc_ep_t *ep)
{
	depctl_data_t 		depctl = { .d32 = 0 };
	volatile uint32_t 	*addr;
	

	if(ep->is_in) {
		addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl;
	} else {	
		addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl;
	}	


	if(core_if->dma_enable == 0 || core_if->dma_desc_enable!= 0) {
		return;
	} else {
		deptsiz_data_t		deptsiz = { .d32 = 0 };
		
		ep->xfer_len = ep->data_per_frame * ep->buf_proc_intrvl / ep->bInterval;
		ep->pkt_cnt = (ep->xfer_len - 1 + ep->maxpacket) / 
				ep->maxpacket;
		ep->xfer_count = 0;
		ep->xfer_buff = (ep->proc_buf_num) ? ep->xfer_buff1 : ep->xfer_buff0;
		ep->dma_addr = (ep->proc_buf_num) ? ep->dma_addr1 : ep->dma_addr0;
		
		if(ep->is_in) {
			/* Program the transfer size and packet count
			 *	as follows: xfersize = N * maxpacket +
			 *	short_packet pktcnt = N + (short_packet
			 *	exist ? 1 : 0)	
			 */
			deptsiz.b.mc = ep->pkt_per_frm;
			deptsiz.b.xfersize = ep->xfer_len;
			deptsiz.b.pktcnt =
				(ep->xfer_len - 1 + ep->maxpacket) / 
				ep->maxpacket;
			dwc_write_reg32(&core_if->dev_if->in_ep_regs[ep->num]->dieptsiz, deptsiz.d32);
	
			/* Write the DMA register */
			dwc_write_reg32 (&(core_if->dev_if->in_ep_regs[ep->num]->diepdma), (uint32_t)ep->dma_addr);
			
		} else {
			deptsiz.b.pktcnt = 
					(ep->xfer_len + (ep->maxpacket - 1)) /
					ep->maxpacket;
			deptsiz.b.xfersize = deptsiz.b.pktcnt * ep->maxpacket;
	
			dwc_write_reg32(&core_if->dev_if->out_ep_regs[ep->num]->doeptsiz, deptsiz.d32);
	
			/* Write the DMA register */
			dwc_write_reg32 (&(core_if->dev_if->out_ep_regs[ep->num]->doepdma), (uint32_t)ep->dma_addr);
			
		}
		/** Enable endpoint, clear nak  */
		depctl.d32 = 0;	
		dwc_modify_reg32(addr, depctl.d32,depctl.d32);
		
		depctl.b.epena = 1;
		depctl.b.cnak = 1;
			
		dwc_modify_reg32(addr, depctl.d32,depctl.d32);
	}
}


/**
 * This function does the setup for a data transfer for an EP and
 * starts the transfer.	 For an IN transfer, the packets will be
 * loaded into the appropriate Tx FIFO in the ISR. For OUT transfers,
 * the packets are unloaded from the Rx FIFO in the ISR.  the ISR.
 *
 * @param core_if Programming view of DWC_otg controller.
 * @param ep The EP to start the transfer on.
 */

void dwc_otg_iso_ep_start_transfer(dwc_otg_core_if_t *core_if, dwc_ep_t *ep)
{
 	if(core_if->dma_enable) {
		if(core_if->dma_desc_enable) {
			if(ep->is_in) {
				ep->desc_cnt = ep->pkt_cnt / ep->pkt_per_frm;
			} else {
				ep->desc_cnt = ep->pkt_cnt;
			}
			dwc_otg_iso_ep_start_ddma_transfer(core_if, ep);
		} else {
			if(core_if->pti_enh_enable) {
				dwc_otg_iso_ep_start_buf_transfer(core_if, ep);
			} else {
				ep->cur_pkt_addr = (ep->proc_buf_num) ? ep->xfer_buff1 : ep->xfer_buff0;
				ep->cur_pkt_dma_addr = (ep->proc_buf_num) ? ep->dma_addr1 : ep->dma_addr0;
				dwc_otg_iso_ep_start_frm_transfer(core_if, ep);
			}
		}
	} else {
		ep->cur_pkt_addr = (ep->proc_buf_num) ? ep->xfer_buff1 : ep->xfer_buff0;
		ep->cur_pkt_dma_addr = (ep->proc_buf_num) ? ep->dma_addr1 : ep->dma_addr0;
		dwc_otg_iso_ep_start_frm_transfer(core_if, ep);									
	}
}

/**
 * This function does the setup for a data transfer for an EP and
 * starts the transfer.	 For an IN transfer, the packets will be
 * loaded into the appropriate Tx FIFO in the ISR. For OUT transfers,
 * the packets are unloaded from the Rx FIFO in the ISR.  the ISR.
 *
 * @param core_if Programming view of DWC_otg controller.
 * @param ep The EP to start the transfer on.
 */

void dwc_otg_iso_ep_stop_transfer(dwc_otg_core_if_t *core_if, dwc_ep_t *ep)
{
	depctl_data_t depctl = { .d32 = 0 };
	volatile uint32_t *addr;

	if(ep->is_in == 1) {
		addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl;
	}
	else {
		addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl;
	}
	
	/* disable the ep */
	depctl.d32 = dwc_read_reg32(addr);

	depctl.b.epdis = 1;
	depctl.b.snak = 1;
	
	dwc_write_reg32(addr, depctl.d32);
	
	if(core_if->dma_desc_enable &&
		ep->iso_desc_addr && ep->iso_dma_desc_addr) {
		dwc_otg_ep_free_desc_chain(ep->iso_desc_addr,ep->iso_dma_desc_addr,ep->desc_cnt * 2);
	}
	
	/* reset varibales */
	ep->dma_addr0 = 0;
	ep->dma_addr1 = 0;
	ep->xfer_buff0 = 0;
	ep->xfer_buff1 = 0;
	ep->data_per_frame = 0;
	ep->data_pattern_frame = 0;
	ep->sync_frame = 0;
	ep->buf_proc_intrvl = 0;
	ep->bInterval = 0;
	ep->proc_buf_num = 0;
	ep->pkt_per_frm = 0;
	ep->pkt_per_frm = 0;
	ep->desc_cnt = 	0;
	ep->iso_desc_addr = 0;
	ep->iso_dma_desc_addr = 0;
}


/**
 * This function is used to submit an ISOC Transfer Request to an EP.
 *
 *	- Every time a sync period completes the request's completion callback
 *	  is called to provide data to the gadget driver.
 *	- Once submitted the request cannot be modified.
 *	- Each request is turned into periodic data packets untill ISO 
 *	  Transfer is stopped..
 */
static int dwc_otg_pcd_iso_ep_start(struct usb_ep *usb_ep, struct usb_iso_request *req, 
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
				int gfp_flags
#else
				gfp_t gfp_flags
#endif
)
{
	dwc_otg_pcd_ep_t 	*ep;
	dwc_otg_pcd_t		*pcd;
	dwc_ep_t 		*dwc_ep;
	unsigned long 		flags = 0;
	int32_t 		frm_data;
	dwc_otg_core_if_t	*core_if;
	dcfg_data_t		dcfg;
	dsts_data_t		dsts;


	if (!req || !req->process_buffer || !req->buf0 || !req->buf1) {
		DWC_WARN("%s, bad params\n", __func__);
		return -EINVAL;
	}

	ep = container_of(usb_ep, dwc_otg_pcd_ep_t, ep);

	if (!usb_ep || !ep->desc || ep->dwc_ep.num == 0) {
		DWC_WARN("%s, bad ep\n", __func__);
		return -EINVAL;
	}

	pcd = ep->pcd;
	core_if = GET_CORE_IF(pcd);
	
	dcfg.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dcfg);
	
	if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) {
		DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n", pcd->gadget.speed);
		DWC_WARN("%s, bogus device state\n", __func__);
		return -ESHUTDOWN;
	}

	SPIN_LOCK_IRQSAVE(&ep->pcd->lock, flags);

	dwc_ep = &ep->dwc_ep;

	if(ep->iso_req) {
		DWC_WARN("%s, iso request in progress\n", __func__);
	}		
	req->status = -EINPROGRESS;

	dwc_ep->dma_addr0 = req->dma0;
	dwc_ep->dma_addr1 = req->dma1;
	
	dwc_ep->xfer_buff0 = req->buf0;
	dwc_ep->xfer_buff1 = req->buf1;

	ep->iso_req = req;
	
	dwc_ep->data_per_frame = req->data_per_frame;

	/** @todo - pattern data support is to be implemented in the future */
	dwc_ep->data_pattern_frame = req->data_pattern_frame;
	dwc_ep->sync_frame = req->sync_frame;
	
	dwc_ep->buf_proc_intrvl = req->buf_proc_intrvl;

	dwc_ep->bInterval = 1 << (ep->desc->bInterval - 1);
	
	dwc_ep->proc_buf_num = 0;
	
	dwc_ep->pkt_per_frm = 0;
	frm_data = ep->dwc_ep.data_per_frame;
	while(frm_data > 0) {
		dwc_ep->pkt_per_frm++;
		frm_data -= ep->dwc_ep.maxpacket;
	}

	dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts);

	if(req->flags & USB_REQ_ISO_ASAP) {
		dwc_ep->next_frame = dsts.b.soffn + 1;
		if(dwc_ep->bInterval != 1){
			dwc_ep->next_frame = dwc_ep->next_frame + (dwc_ep->bInterval - 1 - dwc_ep->next_frame % dwc_ep->bInterval);
		}
	} else {
		dwc_ep->next_frame = req->start_frame;
	}

		
	if(!core_if->pti_enh_enable) { 
		dwc_ep->pkt_cnt = dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm / dwc_ep->bInterval;
	} else {
		dwc_ep->pkt_cnt = 	
			(dwc_ep->data_per_frame * (dwc_ep->buf_proc_intrvl / dwc_ep->bInterval)
			- 1 + dwc_ep->maxpacket) / dwc_ep->maxpacket;
	}
	
	if(core_if->dma_desc_enable) {
		dwc_ep->desc_cnt = 
			dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm / dwc_ep->bInterval;
	}
	
	dwc_ep->pkt_info = kmalloc(sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt, GFP_KERNEL);
	if(!dwc_ep->pkt_info) {
		return -ENOMEM;			
	}
	if(core_if->pti_enh_enable) {
		memset(dwc_ep->pkt_info, 0, sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt);
	}
	
	dwc_ep->cur_pkt = 0;
	
	SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags);

	dwc_otg_iso_ep_start_transfer(core_if, dwc_ep);

	return 0;
}

/**
 * This function stops ISO EP Periodic Data Transfer.
 */
static int dwc_otg_pcd_iso_ep_stop(struct usb_ep *usb_ep, struct usb_iso_request *req)
{
	dwc_otg_pcd_ep_t *ep;
	dwc_otg_pcd_t	*pcd;
	dwc_ep_t *dwc_ep;
	unsigned long flags;
 
	ep = container_of(usb_ep, dwc_otg_pcd_ep_t, ep);
	
	if (!usb_ep || !ep->desc || ep->dwc_ep.num == 0) {
		DWC_WARN("%s, bad ep\n", __func__);
		return -EINVAL;
	}
 	
	pcd = ep->pcd;
	
	if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) {
		DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n", pcd->gadget.speed);
		DWC_WARN("%s, bogus device state\n", __func__);
		return -ESHUTDOWN;
	}
	
	dwc_ep = &ep->dwc_ep;

	dwc_otg_iso_ep_stop_transfer(GET_CORE_IF(pcd), dwc_ep);

	kfree(dwc_ep->pkt_info);

	SPIN_LOCK_IRQSAVE(&pcd->lock, flags);

	if(ep->iso_req != req) {
		return -EINVAL;
	}

	req->status = -ECONNRESET;
		
	SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags);

	
	ep->iso_req = 0;
	
	return 0;
}

/**
 * This function is used for perodical data exchnage between PCD and gadget drivers.
 * for Isochronous EPs
 *
 *	- Every time a sync period completes this function is called to 
 *	  perform data exchange between PCD and gadget
 */
void dwc_otg_iso_buffer_done(dwc_otg_pcd_ep_t *ep, dwc_otg_pcd_iso_request_t *req)
{
 	int i;
	struct usb_gadget_iso_packet_descriptor *iso_packet;
	dwc_ep_t *dwc_ep;

	dwc_ep = &ep->dwc_ep;

	if(ep->iso_req->status == -ECONNRESET) {
		DWC_PRINT("Device has already disconnected\n");
		/*Device has been disconnected*/
		return;
	}
	
	if(dwc_ep->proc_buf_num != 0) {			
		iso_packet = ep->iso_req->iso_packet_desc0;
	}
	
	else {			
		iso_packet = ep->iso_req->iso_packet_desc1;
	}

	/* Fill in ISOC packets descriptors & pass to gadget driver*/
	
	for(i = 0; i < dwc_ep->pkt_cnt; ++i) {
		iso_packet[i].status = dwc_ep->pkt_info[i].status;
		iso_packet[i].offset = dwc_ep->pkt_info[i].offset;
		iso_packet[i].actual_length = dwc_ep->pkt_info[i].length;
		dwc_ep->pkt_info[i].status = 0;
		dwc_ep->pkt_info[i].offset = 0;
		dwc_ep->pkt_info[i].length = 0;
	}

	/* Call callback function to process data buffer */
	ep->iso_req->status = 0;/* success */

	SPIN_UNLOCK(&ep->pcd->lock);
	ep->iso_req->process_buffer(&ep->ep, ep->iso_req);
	SPIN_LOCK(&ep->pcd->lock);
}
 
 
static struct usb_iso_request *dwc_otg_pcd_alloc_iso_request(struct usb_ep *ep,int packets,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
				int gfp_flags
#else
				gfp_t gfp_flags
#endif
)
{
	struct usb_iso_request	*pReq = NULL;
	uint32_t		req_size;

	
	req_size = sizeof(struct usb_iso_request);
	req_size += (2 * packets * (sizeof(struct usb_gadget_iso_packet_descriptor)));

	
	pReq = kmalloc(req_size, gfp_flags);
	if (!pReq) {
		DWC_WARN("%s, can't allocate Iso Request\n", __func__);
		return 0;
	}
	pReq->iso_packet_desc0 = (void*) (pReq +  1);
	
	pReq->iso_packet_desc1 = pReq->iso_packet_desc0 + packets;

	return pReq;
}

static void dwc_otg_pcd_free_iso_request(struct usb_ep *ep, struct usb_iso_request *req)
{
	kfree(req);
}
 
static struct usb_isoc_ep_ops dwc_otg_pcd_ep_ops = 
{
	.ep_ops =
	{
		.enable		= dwc_otg_pcd_ep_enable,
		.disable	= dwc_otg_pcd_ep_disable,
	
		.alloc_request	= dwc_otg_pcd_alloc_request,
		.free_request	= dwc_otg_pcd_free_request,
	
		.alloc_buffer	= dwc_otg_pcd_alloc_buffer,
		.free_buffer	= dwc_otg_pcd_free_buffer,
	
		.queue		= dwc_otg_pcd_ep_queue,
		.dequeue	= dwc_otg_pcd_ep_dequeue,
	
		.set_halt	= dwc_otg_pcd_ep_set_halt,
		.fifo_status	= 0,
		.fifo_flush = 0,
	},
	.iso_ep_start		= dwc_otg_pcd_iso_ep_start,
	.iso_ep_stop		= dwc_otg_pcd_iso_ep_stop,
	.alloc_iso_request 	= dwc_otg_pcd_alloc_iso_request,
	.free_iso_request	= dwc_otg_pcd_free_iso_request,
};

#else

 
static struct usb_ep_ops dwc_otg_pcd_ep_ops = 
{
	.enable		= dwc_otg_pcd_ep_enable,
	.disable	= dwc_otg_pcd_ep_disable,

	.alloc_request	= dwc_otg_pcd_alloc_request,
	.free_request	= dwc_otg_pcd_free_request,

	.alloc_buffer	= dwc_otg_pcd_alloc_buffer,
	.free_buffer	= dwc_otg_pcd_free_buffer,

	.queue		= dwc_otg_pcd_ep_queue,
	.dequeue	= dwc_otg_pcd_ep_dequeue,

	.set_halt	= dwc_otg_pcd_ep_set_halt,
	.fifo_status	= 0,
	.fifo_flush = 0,

	
};

#endif /* DWC_EN_ISOC */
/*	Gadget Operations */
/**
 * The following gadget operations will be implemented in the DWC_otg
 * PCD. Functions in the API that are not described below are not
 * implemented.
 *
 * The Gadget API provides wrapper functions for each of the function
 * pointers defined in usb_gadget_ops. The Gadget Driver calls the
 * wrapper function, which then calls the underlying PCD function. The
 * following sections are named according to the wrapper functions
 * (except for ioctl, which doesn't have a wrapper function). Within
 * each section, the corresponding DWC_otg PCD function name is
 * specified.
 *
 */

/**
 *Gets the USB Frame number of the last SOF.
 */
static int dwc_otg_pcd_get_frame(struct usb_gadget *gadget)
{
	dwc_otg_pcd_t *pcd;
	
	DWC_DEBUGPL(DBG_PCDV,"%s(%p)\n", __func__, gadget);
		
	if (gadget == 0) {
		return -ENODEV;
	} 
	else {
		pcd = container_of(gadget, dwc_otg_pcd_t, gadget);
		dwc_otg_get_frame_number(GET_CORE_IF(pcd));
	}
		
	return 0;
}

void dwc_otg_pcd_initiate_srp(dwc_otg_pcd_t *pcd)
{
	uint32_t *addr = (uint32_t *)&(GET_CORE_IF(pcd)->core_global_regs->gotgctl);
	gotgctl_data_t mem;
	gotgctl_data_t val;
		
	val.d32 = dwc_read_reg32(addr);
	if (val.b.sesreq) {
		DWC_ERROR("Session Request Already active!\n");
			return;
	}

	DWC_NOTICE("Session Request Initated\n");
	mem.d32 = dwc_read_reg32(addr);
	mem.b.sesreq = 1;
	dwc_write_reg32(addr, mem.d32);

	/* Start the SRP timer */
	dwc_otg_pcd_start_srp_timer(pcd);
	return;
}

void dwc_otg_pcd_remote_wakeup(dwc_otg_pcd_t *pcd, int set)
{
	dctl_data_t dctl = {.d32=0};
	volatile uint32_t *addr = &(GET_CORE_IF(pcd)->dev_if->dev_global_regs->dctl);

	if (dwc_otg_is_device_mode(GET_CORE_IF(pcd))) {
		if (pcd->remote_wakeup_enable) {
			if (set) {
				dctl.b.rmtwkupsig = 1;
				dwc_modify_reg32(addr, 0, dctl.d32);
				DWC_DEBUGPL(DBG_PCD, "Set Remote Wakeup\n");
				mdelay(1);
				dwc_modify_reg32(addr, dctl.d32, 0);
				DWC_DEBUGPL(DBG_PCD, "Clear Remote Wakeup\n");
			}
			else {
			}
		}
		else {
			DWC_DEBUGPL(DBG_PCD, "Remote Wakeup is disabled\n");
		}
	}
	return;
}

/**
 * Initiates Session Request Protocol (SRP) to wakeup the host if no
 * session is in progress. If a session is already in progress, but
 * the device is suspended, remote wakeup signaling is started.
 *
 */
static int dwc_otg_pcd_wakeup(struct usb_gadget *gadget)
{
	unsigned long flags;
	dwc_otg_pcd_t *pcd;
	dsts_data_t		dsts;
	gotgctl_data_t	gotgctl;
		
	DWC_DEBUGPL(DBG_PCDV,"%s(%p)\n", __func__, gadget);
		
	if (gadget == 0) {
		return -ENODEV;
	} 
	else {
		pcd = container_of(gadget, dwc_otg_pcd_t, gadget);
	}
	SPIN_LOCK_IRQSAVE(&pcd->lock, flags);

	/*
	 * This function starts the Protocol if no session is in progress. If
	 * a session is already in progress, but the device is suspended,
	 * remote wakeup signaling is started.
	 */

	/* Check if valid session */
	gotgctl.d32 = dwc_read_reg32(&(GET_CORE_IF(pcd)->core_global_regs->gotgctl));
	if (gotgctl.b.bsesvld) {
		/* Check if suspend state */
		dsts.d32 = dwc_read_reg32(&(GET_CORE_IF(pcd)->dev_if->dev_global_regs->dsts));
		if (dsts.b.suspsts) {
			dwc_otg_pcd_remote_wakeup(pcd, 1);
		}
	}
	else {
		dwc_otg_pcd_initiate_srp(pcd);
	}

	SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags);
	return 0;
}

static const struct usb_gadget_ops dwc_otg_pcd_ops = 
{
	.get_frame	 = dwc_otg_pcd_get_frame,
	.wakeup		 = dwc_otg_pcd_wakeup,
	// current versions must always be self-powered
};

/**
 * This function updates the otg values in the gadget structure. 
 */
void dwc_otg_pcd_update_otg(dwc_otg_pcd_t *pcd, const unsigned reset)
{
		
	if (!pcd->gadget.is_otg)
		return;

	if (reset) {
		pcd->b_hnp_enable = 0;
		pcd->a_hnp_support = 0;
		pcd->a_alt_hnp_support = 0;
	}

	pcd->gadget.b_hnp_enable = pcd->b_hnp_enable;
	pcd->gadget.a_hnp_support =  pcd->a_hnp_support;
	pcd->gadget.a_alt_hnp_support = pcd->a_alt_hnp_support;
}

/** 
 * This function is the top level PCD interrupt handler.
 */
static irqreturn_t dwc_otg_pcd_irq(int irq, void *dev
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
				   , struct pt_regs *r
#endif
				 )
{
	dwc_otg_pcd_t *pcd = dev;
	int32_t retval = IRQ_NONE;

	retval = dwc_otg_pcd_handle_intr(pcd);
	return IRQ_RETVAL(retval);
}

/**
 * PCD Callback function for initializing the PCD when switching to
 * device mode.
 *
 * @param p void pointer to the <code>dwc_otg_pcd_t</code>
 */
static int32_t dwc_otg_pcd_start_cb(void *p)
{
	dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *)p;
	
	/*
	 * Initialized the Core for Device mode.
	 */
	if (dwc_otg_is_device_mode(GET_CORE_IF(pcd))) {
		dwc_otg_core_dev_init(GET_CORE_IF(pcd));
	}
	return 1;
}

/**
 * PCD Callback function for stopping the PCD when switching to Host
 * mode.
 *
 * @param p void pointer to the <code>dwc_otg_pcd_t</code>
 */
static int32_t dwc_otg_pcd_stop_cb(void *p)
{
	dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *)p;
	extern void dwc_otg_pcd_stop(dwc_otg_pcd_t *_pcd);
	
	dwc_otg_pcd_stop(pcd);
	return 1;
}


/**
 * PCD Callback function for notifying the PCD when resuming from
 * suspend.
 *
 * @param p void pointer to the <code>dwc_otg_pcd_t</code>
 */
static int32_t dwc_otg_pcd_suspend_cb(void *p)
{
	dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *)p;

	if (pcd->driver && pcd->driver->resume) {
		SPIN_UNLOCK(&pcd->lock);
		pcd->driver->suspend(&pcd->gadget);
		SPIN_LOCK(&pcd->lock);
	}
	
	return 1;
}


/**
 * PCD Callback function for notifying the PCD when resuming from
 * suspend.
 *
 * @param p void pointer to the <code>dwc_otg_pcd_t</code>
 */
static int32_t dwc_otg_pcd_resume_cb(void *p)
{
	dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *)p;
	
	if (pcd->driver && pcd->driver->resume) {
			SPIN_UNLOCK(&pcd->lock);
			pcd->driver->resume(&pcd->gadget);
			SPIN_LOCK(&pcd->lock);
	}
	
	/* Stop the SRP timeout timer. */
	if ((GET_CORE_IF(pcd)->core_params->phy_type != DWC_PHY_TYPE_PARAM_FS) ||
		(!GET_CORE_IF(pcd)->core_params->i2c_enable)) {
		if (GET_CORE_IF(pcd)->srp_timer_started) {
			GET_CORE_IF(pcd)->srp_timer_started = 0;
			del_timer(&pcd->srp_timer);
		}
	}
	return 1;
}


/**
 * PCD Callback structure for handling mode switching.
 */
static dwc_otg_cil_callbacks_t pcd_callbacks = 
{
	.start = dwc_otg_pcd_start_cb,
	.stop = dwc_otg_pcd_stop_cb,
	.suspend = dwc_otg_pcd_suspend_cb,
	.resume_wakeup = dwc_otg_pcd_resume_cb,
	.p = 0, /* Set at registration */
};

/**
 * This function is called when the SRP timer expires.	The SRP should
 * complete within 6 seconds. 
 */
static void srp_timeout(unsigned long ptr)
{
	gotgctl_data_t gotgctl;
	dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *)ptr;
	volatile uint32_t *addr = &core_if->core_global_regs->gotgctl;

	gotgctl.d32 = dwc_read_reg32(addr);

	core_if->srp_timer_started = 0;

	if ((core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) && 
		(core_if->core_params->i2c_enable)) {
		DWC_PRINT("SRP Timeout\n");

		if ((core_if->srp_success) && 
			(gotgctl.b.bsesvld)) {
			if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
				core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
			}
			
			/* Clear Session Request */
			gotgctl.d32 = 0;
			gotgctl.b.sesreq = 1;
			dwc_modify_reg32(&core_if->core_global_regs->gotgctl, 
					  gotgctl.d32, 0);
	
			core_if->srp_success = 0;
		}
		else {
			DWC_ERROR("Device not connected/responding\n");
			gotgctl.b.sesreq = 0;
			dwc_write_reg32(addr, gotgctl.d32);
		}
	}
	else if (gotgctl.b.sesreq) {
		DWC_PRINT("SRP Timeout\n");

		DWC_ERROR("Device not connected/responding\n");
		gotgctl.b.sesreq = 0;
		dwc_write_reg32(addr, gotgctl.d32);
	} 
	else {
		DWC_PRINT(" SRP GOTGCTL=%0x\n", gotgctl.d32);
	} 
}

/**
 * Start the SRP timer to detect when the SRP does not complete within 
 * 6 seconds.
 *
 * @param pcd the pcd structure.
 */
void dwc_otg_pcd_start_srp_timer(dwc_otg_pcd_t *pcd)
{
	struct timer_list *srp_timer = &pcd->srp_timer;
	GET_CORE_IF(pcd)->srp_timer_started = 1;
	init_timer(srp_timer);
	srp_timer->function = srp_timeout;
	srp_timer->data = (unsigned long)GET_CORE_IF(pcd);
	srp_timer->expires = jiffies + (HZ*6);
	add_timer(srp_timer);
}

/**
 * Tasklet
 *
 */
extern void start_next_request(dwc_otg_pcd_ep_t *ep);

static void start_xfer_tasklet_func (unsigned long data)
{
	dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t*)data;
	dwc_otg_core_if_t *core_if = pcd->otg_dev->core_if;

	int i;
	depctl_data_t diepctl;

	DWC_DEBUGPL(DBG_PCDV, "Start xfer tasklet\n");

	diepctl.d32 = dwc_read_reg32(&core_if->dev_if->in_ep_regs[0]->diepctl);

	if (pcd->ep0.queue_sof) {
		pcd->ep0.queue_sof = 0;
		start_next_request (&pcd->ep0);
		// break;
	}

	for (i=0; i<core_if->dev_if->num_in_eps; i++) 
	{
		depctl_data_t diepctl;
		diepctl.d32 = dwc_read_reg32(&core_if->dev_if->in_ep_regs[i]->diepctl);

		if (pcd->in_ep[i].queue_sof) {
			pcd->in_ep[i].queue_sof = 0;
			start_next_request (&pcd->in_ep[i]);
			// break;
		}
	}

	return;
}







static struct tasklet_struct start_xfer_tasklet = {
	.next = NULL,
	.state = 0,
	.count = ATOMIC_INIT(0),
	.func = start_xfer_tasklet_func,
	.data = 0,
};
/**
 * This function initialized the pcd Dp structures to there default
 * state.
 *
 * @param pcd the pcd structure.
 */
void dwc_otg_pcd_reinit(dwc_otg_pcd_t *pcd)
{
	static const char * names[] = 
		{
			
			"ep0",
			"ep1in",	
			"ep2in",	
			"ep3in",	
			"ep4in",	
			"ep5in",	
			"ep6in",	
			"ep7in",	
			"ep8in",	
			"ep9in",	
			"ep10in",	
			"ep11in",	
			"ep12in",	
			"ep13in",	
			"ep14in",	
			"ep15in",	
			"ep1out",  
			"ep2out",  
			"ep3out",
			"ep4out",
			"ep5out",
			"ep6out",
			"ep7out",
			"ep8out",
			"ep9out",
			"ep10out",
			"ep11out",
			"ep12out",
			"ep13out",
			"ep14out",
			"ep15out"
			
	};
		
	int i;
	int in_ep_cntr, out_ep_cntr;
	uint32_t hwcfg1;
	uint32_t num_in_eps = (GET_CORE_IF(pcd))->dev_if->num_in_eps;
	uint32_t num_out_eps = (GET_CORE_IF(pcd))->dev_if->num_out_eps;
	dwc_otg_pcd_ep_t *ep;
	
	DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, pcd);
	
	INIT_LIST_HEAD (&pcd->gadget.ep_list);
	pcd->gadget.ep0 = &pcd->ep0.ep;
	pcd->gadget.speed = USB_SPEED_UNKNOWN;

	INIT_LIST_HEAD (&pcd->gadget.ep0->ep_list);

	/**
	 * Initialize the EP0 structure.
	 */
	ep = &pcd->ep0;

	/* Init EP structure */
	ep->desc = 0;
	ep->pcd = pcd;
	ep->stopped = 1;

	/* Init DWC ep structure */
	ep->dwc_ep.num = 0;
	ep->dwc_ep.active = 0;
	ep->dwc_ep.tx_fifo_num = 0;
	/* Control until ep is actvated */
	ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
	ep->dwc_ep.maxpacket = MAX_PACKET_SIZE;
	ep->dwc_ep.dma_addr = 0;
	ep->dwc_ep.start_xfer_buff = 0;
	ep->dwc_ep.xfer_buff = 0;
	ep->dwc_ep.xfer_len = 0;
	ep->dwc_ep.xfer_count = 0;
	ep->dwc_ep.sent_zlp = 0;
	ep->dwc_ep.total_len = 0;
	ep->queue_sof = 0;
	ep->dwc_ep.desc_addr = 0;
	ep->dwc_ep.dma_desc_addr = 0;
	

	/* Init the usb_ep structure. */
	ep->ep.name = names[0];
	ep->ep.ops = (struct usb_ep_ops*)&dwc_otg_pcd_ep_ops;

	/**
	 * @todo NGS: What should the max packet size be set to
	 * here?  Before EP type is set?
	 */
	ep->ep.maxpacket = MAX_PACKET_SIZE;

	list_add_tail (&ep->ep.ep_list, &pcd->gadget.ep_list);
		
	INIT_LIST_HEAD (&ep->queue);
	/**
	 * Initialize the EP structures.
	 */
	in_ep_cntr = 0;
	hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1.d32 >> 3;
	 
	for (i = 1; in_ep_cntr < num_in_eps; i++) 
	{
		if((hwcfg1 & 0x1) == 0) {
			dwc_otg_pcd_ep_t *ep = &pcd->in_ep[in_ep_cntr];
			in_ep_cntr ++;
			
			/* Init EP structure */
			ep->desc = 0;
			ep->pcd = pcd;
			ep->stopped = 1;
	
			/* Init DWC ep structure */
			ep->dwc_ep.is_in = 1;
			ep->dwc_ep.num = i;
			ep->dwc_ep.active = 0;
			ep->dwc_ep.tx_fifo_num = 0;
			
			/* Control until ep is actvated */
			ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL; 
			ep->dwc_ep.maxpacket = MAX_PACKET_SIZE;
			ep->dwc_ep.dma_addr = 0;
			ep->dwc_ep.start_xfer_buff = 0;
			ep->dwc_ep.xfer_buff = 0;
			ep->dwc_ep.xfer_len = 0;
			ep->dwc_ep.xfer_count = 0;
			ep->dwc_ep.sent_zlp = 0;
			ep->dwc_ep.total_len = 0;
			ep->queue_sof = 0;
			ep->dwc_ep.desc_addr = 0;
			ep->dwc_ep.dma_desc_addr = 0;
	
			/* Init the usb_ep structure. */
			ep->ep.name = names[i];
			ep->ep.ops = (struct usb_ep_ops*)&dwc_otg_pcd_ep_ops;
			
			/**
			 * @todo NGS: What should the max packet size be set to
			 * here?  Before EP type is set?
			 */
			ep->ep.maxpacket = MAX_PACKET_SIZE;
	
			list_add_tail (&ep->ep.ep_list, &pcd->gadget.ep_list);
				
			INIT_LIST_HEAD (&ep->queue);
		}
		hwcfg1 >>= 2;
	}

	out_ep_cntr = 0;
	hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1.d32 >> 2;

	for (i = 1; out_ep_cntr < num_out_eps; i++) 
	{
		if((hwcfg1 & 0x1) == 0) {
			dwc_otg_pcd_ep_t *ep = &pcd->out_ep[out_ep_cntr];
			out_ep_cntr++;
	
			/* Init EP structure */
			ep->desc = 0;
			ep->pcd = pcd;
			ep->stopped = 1;

			/* Init DWC ep structure */
			ep->dwc_ep.is_in = 0;
			ep->dwc_ep.num = i;
			ep->dwc_ep.active = 0;
			ep->dwc_ep.tx_fifo_num = 0;
			/* Control until ep is actvated */
			ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL; 
			ep->dwc_ep.maxpacket = MAX_PACKET_SIZE;
			ep->dwc_ep.dma_addr = 0;
			ep->dwc_ep.start_xfer_buff = 0;
			ep->dwc_ep.xfer_buff = 0;
			ep->dwc_ep.xfer_len = 0;
			ep->dwc_ep.xfer_count = 0;
			ep->dwc_ep.sent_zlp = 0;
			ep->dwc_ep.total_len = 0;
			ep->queue_sof = 0;
	
			/* Init the usb_ep structure. */
			ep->ep.name = names[15 + i];
			ep->ep.ops = (struct usb_ep_ops*)&dwc_otg_pcd_ep_ops;
			/**
			 * @todo NGS: What should the max packet size be set to
			 * here?  Before EP type is set?
			 */
			ep->ep.maxpacket = MAX_PACKET_SIZE;
	
			list_add_tail (&ep->ep.ep_list, &pcd->gadget.ep_list);
				
			INIT_LIST_HEAD (&ep->queue);
		}
		hwcfg1 >>= 2;
	}
	
	/* remove ep0 from the list.  There is a ep0 pointer.*/
	list_del_init (&pcd->ep0.ep.ep_list);
   
	pcd->ep0state = EP0_DISCONNECT;
	pcd->ep0.ep.maxpacket = MAX_EP0_SIZE;		  
	pcd->ep0.dwc_ep.maxpacket = MAX_EP0_SIZE;
	pcd->ep0.dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
}

/**
 * This function releases the Gadget device.
 * required by device_unregister().
 *
 * @todo Should this do something?	Should it free the PCD? 
 */
static void dwc_otg_pcd_gadget_release(struct device *dev)
{
	DWC_DEBUGPL(DBG_PCDV,"%s(%p)\n", __func__, dev);
}



/** 
 * This function initialized the PCD portion of the driver.
 *
 */
 
int dwc_otg_pcd_init(struct lm_device *lmdev)
{
	static char pcd_name[] = "dwc_otg_pcd";
	dwc_otg_pcd_t *pcd;
	dwc_otg_core_if_t* core_if;
	dwc_otg_dev_if_t* dev_if;
	dwc_otg_device_t *otg_dev = lm_get_drvdata(lmdev);
	int retval = 0;
	
	
	DWC_DEBUGPL(DBG_PCDV,"%s(%p)\n",__func__, lmdev);
	/*
	 * Allocate PCD structure
	 */
	pcd = kmalloc(sizeof(dwc_otg_pcd_t), GFP_KERNEL);
	
	if (pcd == 0) {
		return -ENOMEM;
	}
	
	memset(pcd, 0, sizeof(dwc_otg_pcd_t));
	spin_lock_init(&pcd->lock);
	
	otg_dev->pcd = pcd;
	s_pcd = pcd;
	pcd->gadget.name = pcd_name;
	strcpy(pcd->gadget.dev.bus_id, "gadget");
	
	pcd->otg_dev = lm_get_drvdata(lmdev);
	
	pcd->gadget.dev.parent = &lmdev->dev;
	pcd->gadget.dev.release = dwc_otg_pcd_gadget_release;
	pcd->gadget.ops = &dwc_otg_pcd_ops;
	
	core_if = GET_CORE_IF(pcd);
	dev_if = core_if->dev_if;

	if(core_if->hwcfg4.b.ded_fifo_en) {
		DWC_PRINT("Dedicated Tx FIFOs mode\n");
	}
	else {
		DWC_PRINT("Shared Tx FIFO mode\n");
	}
	
	/* If the module is set to FS or if the PHY_TYPE is FS then the gadget
	 * should not report as dual-speed capable.	 replace the following line
	 * with the block of code below it once the software is debugged for
	 * this.  If is_dualspeed = 0 then the gadget driver should not report
	 * a device qualifier descriptor when queried. */
	if ((GET_CORE_IF(pcd)->core_params->speed == DWC_SPEED_PARAM_FULL) ||
		((GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type == 2) &&
		 (GET_CORE_IF(pcd)->hwcfg2.b.fs_phy_type == 1) &&
		 (GET_CORE_IF(pcd)->core_params->ulpi_fs_ls))) {
		pcd->gadget.is_dualspeed = 0;
	}
	else {
		pcd->gadget.is_dualspeed = 1;
	}
	
	if ((otg_dev->core_if->hwcfg2.b.op_mode == DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE) || 
	(otg_dev->core_if->hwcfg2.b.op_mode == DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST) || 
	(otg_dev->core_if->hwcfg2.b.op_mode == DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) || 
	(otg_dev->core_if->hwcfg2.b.op_mode == DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)) {
		pcd->gadget.is_otg = 0;
	}
	else {
		pcd->gadget.is_otg = 1;
	}
	  
		
	pcd->driver = 0;
	/* Register the gadget device */
	retval = device_register(&pcd->gadget.dev);
	if (retval != 0) {
		kfree (pcd);
		return retval;	
	}


	/*
	 * Initialized the Core for Device mode.
	 */
	if (dwc_otg_is_device_mode(core_if)) {
		dwc_otg_core_dev_init(core_if);
	}

	/*
	 * Initialize EP structures
	 */
	dwc_otg_pcd_reinit(pcd);

	/*
	 * Register the PCD Callbacks. 
	 */
	dwc_otg_cil_register_pcd_callbacks(otg_dev->core_if, &pcd_callbacks, 
						pcd);
	/*
	 * Setup interupt handler
	 */
	DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n", lmdev->irq);
	retval = request_irq(lmdev->irq, dwc_otg_pcd_irq,
				SA_SHIRQ, pcd->gadget.name, pcd);
	if (retval != 0) {
		DWC_ERROR("request of irq%d failed\n", lmdev->irq);
		device_unregister(&pcd->gadget.dev);
		kfree (pcd);
		return -EBUSY;
	}

	/* 
	 * Initialize the DMA buffer for SETUP packets
	 */
	if (GET_CORE_IF(pcd)->dma_enable) {
		pcd->setup_pkt = dma_alloc_coherent (NULL, sizeof (*pcd->setup_pkt) * 5, &pcd->setup_pkt_dma_handle, 0);
		if (pcd->setup_pkt == 0) {
			free_irq(lmdev->irq, pcd);
			device_unregister(&pcd->gadget.dev);
			kfree (pcd);
			return -ENOMEM;
		}

		pcd->status_buf = dma_alloc_coherent (NULL, sizeof (uint16_t), &pcd->status_buf_dma_handle, 0);
		if (pcd->status_buf == 0) {
			dma_free_coherent(NULL, sizeof(*pcd->setup_pkt), pcd->setup_pkt, pcd->setup_pkt_dma_handle);
			free_irq(lmdev->irq, pcd);
			device_unregister(&pcd->gadget.dev);
			kfree (pcd);
			return -ENOMEM;
		}

		if (GET_CORE_IF(pcd)->dma_desc_enable) {
			dev_if->setup_desc_addr[0] = dwc_otg_ep_alloc_desc_chain(&dev_if->dma_setup_desc_addr[0], 1);
			dev_if->setup_desc_addr[1] = dwc_otg_ep_alloc_desc_chain(&dev_if->dma_setup_desc_addr[1], 1);
			dev_if->in_desc_addr = dwc_otg_ep_alloc_desc_chain(&dev_if->dma_in_desc_addr, 1);
			dev_if->out_desc_addr = dwc_otg_ep_alloc_desc_chain(&dev_if->dma_out_desc_addr, 1);

			if(dev_if->setup_desc_addr[0] == 0 
			|| dev_if->setup_desc_addr[1] == 0
			|| dev_if->in_desc_addr == 0
			|| dev_if->out_desc_addr == 0 ) {
				
				if(dev_if->out_desc_addr)
					dwc_otg_ep_free_desc_chain(dev_if->out_desc_addr, dev_if->dma_out_desc_addr, 1);
				if(dev_if->in_desc_addr)
					dwc_otg_ep_free_desc_chain(dev_if->in_desc_addr, dev_if->dma_in_desc_addr, 1);
				if(dev_if->setup_desc_addr[1])
					dwc_otg_ep_free_desc_chain(dev_if->setup_desc_addr[1], dev_if->dma_setup_desc_addr[1], 1);
				if(dev_if->setup_desc_addr[0])
					dwc_otg_ep_free_desc_chain(dev_if->setup_desc_addr[0], dev_if->dma_setup_desc_addr[0], 1);

				
				dma_free_coherent(NULL, sizeof(*pcd->status_buf), pcd->status_buf, pcd->setup_pkt_dma_handle);
				dma_free_coherent(NULL, sizeof(*pcd->setup_pkt), pcd->setup_pkt, pcd->setup_pkt_dma_handle);
				
				free_irq(lmdev->irq, pcd);
				device_unregister(&pcd->gadget.dev);
				kfree (pcd);
				
				return -ENOMEM;
			}
		}
	}
	else {
		pcd->setup_pkt = kmalloc (sizeof (*pcd->setup_pkt) * 5, GFP_KERNEL);
		if (pcd->setup_pkt == 0) {
			free_irq(lmdev->irq, pcd);
			device_unregister(&pcd->gadget.dev);
			kfree (pcd);
			return -ENOMEM;
		}

		pcd->status_buf = kmalloc (sizeof (uint16_t), GFP_KERNEL);
		if (pcd->status_buf == 0) {
			kfree(pcd->setup_pkt);
			free_irq(lmdev->irq, pcd);
			device_unregister(&pcd->gadget.dev);
			kfree (pcd);
			return -ENOMEM;
		}
	}


	/* Initialize tasklet */
	start_xfer_tasklet.data = (unsigned long)pcd;
	pcd->start_xfer_tasklet = &start_xfer_tasklet;

	return 0;
}

/**
 * Cleanup the PCD.
 */
void dwc_otg_pcd_remove(struct lm_device *lmdev)
{
	dwc_otg_device_t *otg_dev = lm_get_drvdata(lmdev);
	dwc_otg_pcd_t *pcd = otg_dev->pcd;
	dwc_otg_dev_if_t* dev_if = GET_CORE_IF(pcd)->dev_if;
	
	DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, lmdev);

	/*
	 * Free the IRQ 
	 */
	free_irq(lmdev->irq, pcd);
	
	 /* start with the driver above us */
	if (pcd->driver) {
		/* should have been done already by driver model core */
		DWC_WARN("driver '%s' is still registered\n",
				 pcd->driver->driver.name);
		usb_gadget_unregister_driver(pcd->driver);
	}
	device_unregister(&pcd->gadget.dev);
		
	if (GET_CORE_IF(pcd)->dma_enable) {
		dma_free_coherent (NULL, sizeof (*pcd->setup_pkt) * 5, pcd->setup_pkt, pcd->setup_pkt_dma_handle);
		dma_free_coherent (NULL, sizeof (uint16_t), pcd->status_buf, pcd->status_buf_dma_handle);
		if (GET_CORE_IF(pcd)->dma_desc_enable) {
			dwc_otg_ep_free_desc_chain(dev_if->setup_desc_addr[0], dev_if->dma_setup_desc_addr[0], 1);
			dwc_otg_ep_free_desc_chain(dev_if->setup_desc_addr[1], dev_if->dma_setup_desc_addr[1], 1);
			dwc_otg_ep_free_desc_chain(dev_if->in_desc_addr, dev_if->dma_in_desc_addr, 1);
			dwc_otg_ep_free_desc_chain(dev_if->out_desc_addr, dev_if->dma_out_desc_addr, 1);
		}
	}
	else {
		kfree (pcd->setup_pkt);
		kfree (pcd->status_buf);
	}
	
	kfree(pcd);
	otg_dev->pcd = 0;
}

/**
 * This function registers a gadget driver with the PCD.
 *
 * When a driver is successfully registered, it will receive control
 * requests including set_configuration(), which enables non-control
 * requests.  then usb traffic follows until a disconnect is reported.
 * then a host may connect again, or the driver might get unbound.
 *
 * @param driver The driver being registered
 */
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
{
	int retval;

	DWC_DEBUGPL(DBG_PCD, "registering gadget driver '%s'\n", driver->driver.name);
		
	if (!driver || driver->speed == USB_SPEED_UNKNOWN || 
		!driver->bind || 
		!driver->unbind || 
		!driver->disconnect || 
		!driver->setup) {
		DWC_DEBUGPL(DBG_PCDV,"EINVAL\n");	
		return -EINVAL;
	}
	if (s_pcd == 0) {
		DWC_DEBUGPL(DBG_PCDV,"ENODEV\n");	
		return -ENODEV;
	}
	if (s_pcd->driver != 0) {
		DWC_DEBUGPL(DBG_PCDV,"EBUSY (%p)\n", s_pcd->driver);   
		return -EBUSY;
	}
		
	/* hook up the driver */
	s_pcd->driver = driver;
	s_pcd->gadget.dev.driver = &driver->driver;

	DWC_DEBUGPL(DBG_PCD, "bind to driver %s\n", driver->driver.name);
	retval = driver->bind(&s_pcd->gadget);
	if (retval) {
		DWC_ERROR("bind to driver %s --> error %d\n",
					driver->driver.name, retval);
		s_pcd->driver = 0;
		s_pcd->gadget.dev.driver = 0;
		return retval;
	}
	DWC_DEBUGPL(DBG_ANY, "registered gadget driver '%s'\n", 
					driver->driver.name);
	return 0;
}

EXPORT_SYMBOL(usb_gadget_register_driver);

/**
 * This function unregisters a gadget driver
 *
 * @param driver The driver being unregistered
 */
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
	//DWC_DEBUGPL(DBG_PCDV,"%s(%p)\n", __func__, _driver);

	if (s_pcd == 0) {
		DWC_DEBUGPL(DBG_ANY, "%s Return(%d): s_pcd==0\n", __func__, 
				-ENODEV);
		return -ENODEV;
	}
	if (driver == 0 || driver != s_pcd->driver) {
		DWC_DEBUGPL(DBG_ANY, "%s Return(%d): driver?\n", __func__, 
				-EINVAL);
		return -EINVAL;
	}

	driver->unbind(&s_pcd->gadget);
	s_pcd->driver = 0;

	DWC_DEBUGPL(DBG_ANY, "unregistered driver '%s'\n", 
			driver->driver.name);
	return 0;
}
EXPORT_SYMBOL(usb_gadget_unregister_driver);

#endif /* DWC_HOST_ONLY */
