--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/omap3530/beagle_drivers/wb/api/src/cyasdma.c Wed Mar 03 13:10:32 2010 +0000
@@ -0,0 +1,1054 @@
+/* Cypress West Bridge API source file (cyasdma.c)
+## ===========================
+##
+## Copyright Cypress Semiconductor Corporation, 2006-2009,
+## All Rights Reserved
+## UNPUBLISHED, LICENSED SOFTWARE.
+##
+## CONFIDENTIAL AND PROPRIETARY INFORMATION
+## WHICH IS THE PROPERTY OF CYPRESS.
+##
+## Use of this file is governed
+## by the license agreement included in the file
+##
+## <install>/license/license.txt
+##
+## where <install> is the Cypress software
+## installation root directory path.
+##
+## ===========================
+*/
+
+#include "cyashal.h"
+#include "cyasdma.h"
+#include "cyaslowlevel.h"
+#include "cyaserr.h"
+#include "cyasregs.h"
+
+/*
+ * Add the DMA queue entry to the free list to be re-used later
+ */
+static void
+CyAsDmaAddRequestToFreeQueue(CyAsDevice *dev_p, CyAsDmaQueueEntry *req_p)
+{
+ uint32_t imask ;
+ imask = CyAsHalDisableInterrupts() ;
+
+ req_p->next_p = dev_p->dma_freelist_p ;
+ dev_p->dma_freelist_p = req_p ;
+
+ CyAsHalEnableInterrupts(imask) ;
+}
+
+/*
+ * Get a DMA queue entry from the free list.
+ */
+static CyAsDmaQueueEntry *
+CyAsDmaGetDmaQueueEntry(CyAsDevice *dev_p)
+{
+ CyAsDmaQueueEntry *req_p ;
+ uint32_t imask ;
+
+ CyAsHalAssert(dev_p->dma_freelist_p != 0) ;
+
+ imask = CyAsHalDisableInterrupts() ;
+ req_p = dev_p->dma_freelist_p ;
+ dev_p->dma_freelist_p = req_p->next_p ;
+ CyAsHalEnableInterrupts(imask) ;
+
+ return req_p ;
+}
+
+/*
+ * Set the maximum size that the West Bridge hardware can handle in a single DMA operation. This size
+ * may change for the P <-> U endpoints as a function of the endpoint type and whether we are running
+ * at full speed or high speed.
+ */
+CyAsReturnStatus_t
+CyAsDmaSetMaxDmaSize(CyAsDevice *dev_p, CyAsEndPointNumber_t ep, uint32_t size)
+{
+ /* In MTP mode, EP2 is allowed to have all max sizes. */
+ if ((!dev_p->is_mtp_firmware) || (ep != 0x02))
+ {
+ if (size < 64 || size > 1024)
+ return CY_AS_ERROR_INVALID_SIZE ;
+ }
+
+ CY_AS_NUM_EP(dev_p, ep)->maxhwdata = (uint16_t)size ;
+ return CY_AS_ERROR_SUCCESS ;
+}
+
+/*
+ * The callback for requests sent to West Bridge to relay endpoint data. Endpoint
+ * data for EP0 and EP1 are sent using mailbox requests. This is the callback that
+ * is called when a response to a mailbox request to send data is received.
+ */
+static void
+CyAsDmaRequestCallback(
+ CyAsDevice *dev_p,
+ uint8_t context,
+ CyAsLLRequestResponse *req_p,
+ CyAsLLRequestResponse *resp_p,
+ CyAsReturnStatus_t ret)
+{
+ uint16_t v ;
+ uint16_t datacnt ;
+ CyAsEndPointNumber_t ep ;
+
+ (void)context ;
+
+ CyAsLogDebugMessage(5, "CyAsDmaRequestCallback called") ;
+
+ /*
+ * Extract the return code from the firmware
+ */
+ if (ret == CY_AS_ERROR_SUCCESS)
+ {
+ if (CyAsLLRequestResponse_GetCode(resp_p) != CY_RESP_SUCCESS_FAILURE)
+ ret = CY_AS_ERROR_INVALID_RESPONSE ;
+ else
+ ret = CyAsLLRequestResponse_GetWord(resp_p, 0) ;
+ }
+
+ /*
+ * Extract the endpoint number and the transferred byte count
+ * from the request.
+ */
+ v = CyAsLLRequestResponse_GetWord(req_p, 0) ;
+ ep = (CyAsEndPointNumber_t)((v >> 13) & 0x01) ;
+
+ if (ret == CY_AS_ERROR_SUCCESS)
+ {
+ /*
+ * If the firmware returns success, all of the data requested was
+ * transferred. There are no partial transfers.
+ */
+ datacnt = v & 0x3FF ;
+ }
+ else
+ {
+ /*
+ * If the firmware returned an error, no data was transferred.
+ */
+ datacnt = 0 ;
+ }
+
+ /*
+ * Queue the request and response data structures for use with the
+ * next EP0 or EP1 request.
+ */
+ if (ep == 0)
+ {
+ dev_p->usb_ep0_dma_req = req_p ;
+ dev_p->usb_ep0_dma_resp = resp_p ;
+ }
+ else
+ {
+ dev_p->usb_ep1_dma_req = req_p ;
+ dev_p->usb_ep1_dma_resp = resp_p ;
+ }
+
+ /*
+ * Call the DMA complete function so we can signal that this portion of the
+ * transfer has completed. If the low level request was canceled, we do not
+ * need to signal the completed function as the only way a cancel can happen
+ * is via the DMA cancel function.
+ */
+ if (ret != CY_AS_ERROR_CANCELED)
+ CyAsDmaCompletedCallback(dev_p->tag, ep, datacnt, ret) ;
+}
+
+/*
+ * Set the DRQ mask register for the given endpoint number. If state is
+ * CyTrue, the DRQ interrupt for the given endpoint is enabled, otherwise
+ * it is disabled.
+ */
+static void
+CyAsDmaSetDrq(CyAsDevice *dev_p, CyAsEndPointNumber_t ep, CyBool state)
+{
+ uint16_t mask ;
+ uint16_t v ;
+ uint32_t intval ;
+
+ /*
+ * There are not DRQ register bits for EP0 and EP1
+ */
+ if (ep == 0 || ep == 1)
+ return ;
+
+ /*
+ * Disable interrupts while we do this to be sure the state of the
+ * DRQ mask register is always well defined.
+ */
+ intval = CyAsHalDisableInterrupts() ;
+
+ /*
+ * Set the DRQ bit to the given state for the ep given
+ */
+ mask = (1 << ep) ;
+ v = CyAsHalReadRegister(dev_p->tag, CY_AS_MEM_P0_DRQ_MASK) ;
+
+ if (state)
+ v |= mask ;
+ else
+ v &= ~mask ;
+
+ CyAsHalWriteRegister(dev_p->tag, CY_AS_MEM_P0_DRQ_MASK, v) ;
+ CyAsHalEnableInterrupts(intval) ;
+}
+
+/*
+* Send the next DMA request for the endpoint given
+*/
+static void
+CyAsDmaSendNextDmaRequest(CyAsDevice *dev_p, CyAsDmaEndPoint *ep_p)
+{
+ uint32_t datacnt ;
+ void *buf_p ;
+ CyAsDmaQueueEntry *dma_p ;
+
+ CyAsLogDebugMessage(6, "CyAsDmaSendNextDmaRequest called") ;
+
+ /* If the queue is empty, nothing to do */
+ dma_p = ep_p->queue_p ;
+ if (dma_p == 0)
+ {
+ /*
+ * There are not pending DMA requests for this endpoint. Disable
+ * the DRQ mask bits to insure no interrupts will be triggered by this
+ * endpoint until someone is interested in the data.
+ */
+ CyAsDmaSetDrq(dev_p, ep_p->ep, CyFalse) ;
+ return ;
+ }
+
+ CyAsDmaEndPointSetRunning(ep_p) ;
+
+ /*
+ * Get the number of words that still need to be xferred in
+ * this request.
+ */
+ datacnt = dma_p->size - dma_p->offset ;
+ CyAsHalAssert(datacnt >= 0) ;
+
+ /*
+ * The HAL layer should never limit the size of the transfer to
+ * something less than the maxhwdata otherwise, the data will be
+ * sent in packets that are not correct in size.
+ */
+ CyAsHalAssert(ep_p->maxhaldata == CY_AS_DMA_MAX_SIZE_HW_SIZE || ep_p->maxhaldata >= ep_p->maxhwdata) ;
+
+ /*
+ * Update the number of words that need to be xferred yet
+ * based on the limits of the HAL layer.
+ */
+ if (ep_p->maxhaldata == CY_AS_DMA_MAX_SIZE_HW_SIZE)
+ {
+ if (datacnt > ep_p->maxhwdata)
+ datacnt = ep_p->maxhwdata ;
+ }
+ else
+ {
+ if (datacnt > ep_p->maxhaldata)
+ datacnt = ep_p->maxhaldata ;
+ }
+
+ /*
+ * Find a pointer to the data that needs to be transferred
+ */
+ buf_p = (((char *)dma_p->buf_p) + dma_p->offset);
+
+ /*
+ * Mark a request in transit
+ */
+ CyAsDmaEndPointSetInTransit(ep_p) ;
+
+ if (ep_p->ep == 0 || ep_p->ep == 1)
+ {
+ /*
+ * If this is a WRITE request on EP0 and EP1, we write the data via an EP_DATA request
+ * to West Bridge via the mailbox registers. If this is a READ request, we do nothing and the data will
+ * arrive via an EP_DATA request from West Bridge. In the request handler for the USB context we will pass
+ * the data back into the DMA module.
+ */
+ if (dma_p->readreq == CyFalse)
+ {
+ uint16_t v ;
+ uint16_t len ;
+ CyAsLLRequestResponse *resp_p ;
+ CyAsLLRequestResponse *req_p ;
+ CyAsReturnStatus_t ret ;
+
+ len = (uint16_t)(datacnt / 2) ;
+ if (datacnt % 2)
+ len++ ;
+
+ len++ ;
+
+ if (ep_p->ep == 0)
+ {
+ req_p = dev_p->usb_ep0_dma_req ;
+ resp_p = dev_p->usb_ep0_dma_resp ;
+ dev_p->usb_ep0_dma_req = 0 ;
+ dev_p->usb_ep0_dma_resp = 0 ;
+ }
+ else
+ {
+ req_p = dev_p->usb_ep1_dma_req ;
+ resp_p = dev_p->usb_ep1_dma_resp ;
+ dev_p->usb_ep1_dma_req = 0 ;
+ dev_p->usb_ep1_dma_resp = 0 ;
+ }
+
+ CyAsHalAssert(req_p != 0) ;
+ CyAsHalAssert(resp_p != 0) ;
+ CyAsHalAssert(len <= 64) ;
+
+ CyAsLLInitRequest(req_p, CY_RQT_USB_EP_DATA, CY_RQT_USB_RQT_CONTEXT, len) ;
+
+ v = (uint16_t)(datacnt | (ep_p->ep << 13) | (1 << 14)) ;
+ if (dma_p->offset == 0)
+ v |= (1 << 12) ; /* Set the first packet bit */
+ if (dma_p->offset + datacnt == dma_p->size)
+ v |= (1 << 11) ; /* Set the last packet bit */
+
+ CyAsLLRequestResponse_SetWord(req_p, 0, v) ;
+ CyAsLLRequestResponse_Pack(req_p, 1, datacnt, buf_p) ;
+
+ CyAsLLInitResponse(resp_p, 1) ;
+
+ ret = CyAsLLSendRequest(dev_p, req_p, resp_p, CyFalse, CyAsDmaRequestCallback) ;
+ if (ret == CY_AS_ERROR_SUCCESS)
+ CyAsLogDebugMessage(5, "+++ Send EP 0/1 data via mailbox registers") ;
+ else
+ CyAsLogDebugMessage(5, "+++ Error Sending EP 0/1 data via mailbox registers - CY_AS_ERROR_TIMEOUT") ;
+
+ if (ret != CY_AS_ERROR_SUCCESS)
+ CyAsDmaCompletedCallback(dev_p->tag, ep_p->ep, 0, ret) ;
+ }
+ }
+ else
+ {
+ /*
+ * This is a DMA request on an endpoint that is accessible via the P port. Ask the
+ * HAL DMA capabilities to perform this. The amount of data sent is limited by the
+ * HAL max size as well as what we need to send. If the ep_p->maxhaldata is set to
+ * a value larger than the endpoint buffer size, then we will pass more than a single
+ * buffer worth of data to the HAL layer and expect the HAL layer to divide the data
+ * into packets. The last parameter here (ep_p->maxhwdata) gives the packet size for
+ * the data so the HAL layer knows what the packet size should be.
+ */
+ if (CyAsDmaEndPointIsDirectionIn(ep_p))
+ CyAsHalDmaSetupWrite(dev_p->tag, ep_p->ep, buf_p, datacnt, ep_p->maxhwdata) ;
+ else
+ CyAsHalDmaSetupRead(dev_p->tag, ep_p->ep, buf_p, datacnt, ep_p->maxhwdata) ;
+
+ /*
+ * The DRQ interrupt for this endpoint should be enabled so that the data
+ * transfer progresses at interrupt time.
+ */
+ CyAsDmaSetDrq(dev_p, ep_p->ep, CyTrue) ;
+ }
+}
+
+/*
+ * This function is called when the HAL layer has completed the last requested DMA
+ * operation. This function sends/receives the next batch of data associated with the
+ * current DMA request, or it is is complete, moves to the next DMA request.
+ */
+void
+CyAsDmaCompletedCallback(CyAsHalDeviceTag tag, CyAsEndPointNumber_t ep, uint32_t cnt, CyAsReturnStatus_t status)
+{
+ uint32_t mask ;
+ CyAsDmaQueueEntry *req_p ;
+ CyAsDmaEndPoint *ep_p ;
+ CyAsDevice *dev_p = CyAsDeviceFindFromTag(tag) ;
+
+ /* Make sure the HAL layer gave us good parameters */
+ CyAsHalAssert(dev_p != 0) ;
+ CyAsHalAssert(dev_p->sig == CY_AS_DEVICE_HANDLE_SIGNATURE) ;
+ CyAsHalAssert(ep < 16) ;
+
+
+ /* Get the endpoint ptr */
+ ep_p = CY_AS_NUM_EP(dev_p, ep) ;
+ CyAsHalAssert(ep_p->queue_p != 0) ;
+
+ /* Get a pointer to the current entry in the queue */
+ mask = CyAsHalDisableInterrupts() ;
+ req_p = ep_p->queue_p ;
+
+ /* Update the offset to reflect the data actually received or sent */
+ req_p->offset += cnt ;
+
+ /*
+ * If we are still sending/receiving the current packet, send/receive the next chunk
+ * Basically we keep going if we have not sent/received enough data, and we are not doing
+ * a packet operation, and the last packet sent or received was a full sized packet. In
+ * other words, when we are NOT doing a packet operation, a less than full size packet
+ * (a short packet) will terminate the operation.
+ *
+ * Note: If this is EP1 request and the request has timed out, it means the buffer is not free.
+ * We have to resend the data.
+ *
+ * Note: For the MTP data transfers, the DMA transfer for the next packet can only be started
+ * asynchronously, after a firmware event notifies that the device is ready.
+ */
+ if (((req_p->offset != req_p->size) && (req_p->packet == CyFalse) && ((cnt == ep_p->maxhaldata) ||
+ ((cnt == ep_p->maxhwdata) && ((ep != CY_AS_MTP_READ_ENDPOINT) || (cnt == dev_p->usb_max_tx_size)))))
+ || ((ep == 1) && (status == CY_AS_ERROR_TIMEOUT)))
+ {
+ CyAsHalEnableInterrupts(mask) ;
+
+ /*
+ * And send the request again to send the next block of data. Special handling for
+ * MTP transfers on EPs 2 and 6. The SendNextRequest will be processed based on the
+ * event sent by the firmware.
+ */
+ if ((ep == CY_AS_MTP_WRITE_ENDPOINT) || (
+ (ep == CY_AS_MTP_READ_ENDPOINT) && (!CyAsDmaEndPointIsDirectionIn (ep_p))))
+ CyAsDmaEndPointSetStopped(ep_p) ;
+ else
+ CyAsDmaSendNextDmaRequest(dev_p, ep_p) ;
+ }
+ else
+ {
+ /*
+ * We get here if ...
+ * we have sent or received all of the data
+ * or
+ * we are doing a packet operation
+ * or
+ * we receive a short packet
+ */
+
+ /*
+ * Remove this entry from the DMA queue for this endpoint.
+ */
+ CyAsDmaEndPointClearInTransit(ep_p) ;
+ ep_p->queue_p = req_p->next_p ;
+ if (ep_p->last_p == req_p)
+ {
+ /*
+ * We have removed the last packet from the DMA queue, disable the
+ * interrupt associated with this interrupt.
+ */
+ ep_p->last_p = 0 ;
+ CyAsHalEnableInterrupts(mask) ;
+ CyAsDmaSetDrq(dev_p, ep, CyFalse) ;
+ }
+ else
+ CyAsHalEnableInterrupts(mask) ;
+
+ if (req_p->cb)
+ {
+ /*
+ * If the request has a callback associated with it, call the callback
+ * to tell the interested party that this DMA request has completed.
+ *
+ * Note, we set the InCallback bit to insure that we cannot recursively
+ * call an API function that is synchronous only from a callback.
+ */
+ CyAsDeviceSetInCallback(dev_p) ;
+ (*req_p->cb)(dev_p, ep, req_p->buf_p, req_p->offset, status) ;
+ CyAsDeviceClearInCallback(dev_p) ;
+ }
+
+ /*
+ * We are done with this request, put it on the freelist to be
+ * reused at a later time.
+ */
+ CyAsDmaAddRequestToFreeQueue(dev_p, req_p) ;
+
+ if (ep_p->queue_p == 0)
+ {
+ /*
+ * If the endpoint is out of DMA entries, set it the endpoint as
+ * stopped.
+ */
+ CyAsDmaEndPointSetStopped(ep_p) ;
+
+ /*
+ * The DMA queue is empty, wake any task waiting on the QUEUE to
+ * drain.
+ */
+ if (CyAsDmaEndPointIsSleeping(ep_p))
+ {
+ CyAsDmaEndPointSetWakeState(ep_p) ;
+ CyAsHalWake(&ep_p->channel) ;
+ }
+ }
+ else
+ {
+ /*
+ * If the queued operation is a MTP transfer, wait until firmware event
+ * before sending down the next DMA request.
+ */
+ if ((ep == CY_AS_MTP_WRITE_ENDPOINT) || (
+ (ep == CY_AS_MTP_READ_ENDPOINT) && (!CyAsDmaEndPointIsDirectionIn (ep_p))))
+ CyAsDmaEndPointSetStopped(ep_p) ;
+ else
+ CyAsDmaSendNextDmaRequest(dev_p, ep_p) ;
+ }
+ }
+}
+
+/*
+* This function is used to kick start DMA on a given channel. If DMA is already running
+* on the given endpoint, nothing happens. If DMA is not running, the first entry is pulled
+* from the DMA queue and sent/recevied to/from the West Bridge device.
+*/
+CyAsReturnStatus_t
+CyAsDmaKickStart(CyAsDevice *dev_p, CyAsEndPointNumber_t ep)
+{
+ CyAsDmaEndPoint *ep_p ;
+ CyAsHalAssert(dev_p->sig == CY_AS_DEVICE_HANDLE_SIGNATURE) ;
+
+ ep_p = CY_AS_NUM_EP(dev_p, ep) ;
+
+ /* We are already running */
+ if (CyAsDmaEndPointIsRunning(ep_p))
+ return CY_AS_ERROR_SUCCESS ;
+
+ CyAsDmaSendNextDmaRequest(dev_p, ep_p);
+ return CY_AS_ERROR_SUCCESS ;
+}
+
+/*
+ * This function stops the given endpoint. Stopping and endpoint cancels
+ * any pending DMA operations and frees all resources associated with the
+ * given endpoint.
+ */
+static CyAsReturnStatus_t
+CyAsDmaStopEndPoint(CyAsDevice *dev_p, CyAsEndPointNumber_t ep)
+{
+ CyAsReturnStatus_t ret ;
+ CyAsDmaEndPoint *ep_p = CY_AS_NUM_EP(dev_p, ep) ;
+
+ /*
+ * Cancel any pending DMA requests associated with this endpoint. This
+ * cancels any DMA requests at the HAL layer as well as dequeues any request
+ * that is currently pending.
+ */
+ ret = CyAsDmaCancel(dev_p, ep, CY_AS_ERROR_CANCELED) ;
+ if (ret != CY_AS_ERROR_SUCCESS)
+ return ret ;
+
+ /*
+ * Destroy the sleep channel
+ */
+ if (!CyAsHalDestroySleepChannel(&ep_p->channel) && ret == CY_AS_ERROR_SUCCESS)
+ ret = CY_AS_ERROR_DESTROY_SLEEP_CHANNEL_FAILED ;
+
+ /*
+ * Free the memory associated with this endpoint
+ */
+ CyAsHalFree(ep_p) ;
+
+ /*
+ * Set the data structure ptr to something sane since the
+ * previous pointer is now free.
+ */
+ dev_p->endp[ep] = 0 ;
+
+ return ret ;
+}
+
+/*
+ * This method stops the USB stack. This is an internal function that does
+ * all of the work of destroying the USB stack without the protections that
+ * we provide to the API (i.e. stopping at stack that is not running).
+ */
+static CyAsReturnStatus_t
+CyAsDmaStopInternal(CyAsDevice *dev_p)
+{
+ CyAsReturnStatus_t ret = CY_AS_ERROR_SUCCESS ;
+ CyAsReturnStatus_t lret ;
+ CyAsEndPointNumber_t i ;
+
+ /*
+ * Stop all of the endpoints. This cancels all DMA requests, and
+ * frees all resources associated with each endpoint.
+ */
+ for(i = 0 ; i < sizeof(dev_p->endp)/(sizeof(dev_p->endp[0])) ; i++)
+ {
+ lret = CyAsDmaStopEndPoint(dev_p, i) ;
+ if (lret != CY_AS_ERROR_SUCCESS && ret == CY_AS_ERROR_SUCCESS)
+ ret = lret ;
+ }
+
+ /*
+ * Now, free the list of DMA requests structures that we use to manage
+ * DMA requests.
+ */
+ while (dev_p->dma_freelist_p)
+ {
+ CyAsDmaQueueEntry *req_p ;
+ uint32_t imask = CyAsHalDisableInterrupts() ;
+
+ req_p = dev_p->dma_freelist_p ;
+ dev_p->dma_freelist_p = req_p->next_p ;
+
+ CyAsHalEnableInterrupts(imask) ;
+
+ CyAsHalFree(req_p) ;
+ }
+
+ CyAsLLDestroyRequest(dev_p, dev_p->usb_ep0_dma_req) ;
+ CyAsLLDestroyRequest(dev_p, dev_p->usb_ep1_dma_req) ;
+ CyAsLLDestroyResponse(dev_p, dev_p->usb_ep0_dma_resp) ;
+ CyAsLLDestroyResponse(dev_p, dev_p->usb_ep1_dma_resp) ;
+
+ return ret ;
+}
+
+
+/*
+ * CyAsDmaStop()
+ *
+ * This function shuts down the DMA module. All resources associated with the DMA module
+ * will be freed. This routine is the API stop function. It insures that we are stopping
+ * a stack that is actually running and then calls the internal function to do the work.
+ */
+CyAsReturnStatus_t
+CyAsDmaStop(CyAsDevice *dev_p)
+{
+ CyAsReturnStatus_t ret ;
+
+ ret = CyAsDmaStopInternal(dev_p) ;
+ CyAsDeviceSetDmaStopped(dev_p) ;
+
+ return ret ;
+}
+
+/*
+ * CyAsDmaStart()
+ *
+ * This function intializes the DMA module to insure it is up and running.
+ */
+CyAsReturnStatus_t
+CyAsDmaStart(CyAsDevice *dev_p)
+{
+ CyAsEndPointNumber_t i ;
+ uint16_t cnt ;
+
+ if (CyAsDeviceIsDmaRunning(dev_p))
+ return CY_AS_ERROR_ALREADY_RUNNING ;
+
+ /*
+ * Pre-allocate DMA Queue structures to be used in the interrupt context
+ */
+ for(cnt = 0 ; cnt < 32 ; cnt++)
+ {
+ CyAsDmaQueueEntry *entry_p = (CyAsDmaQueueEntry *)CyAsHalAlloc(sizeof(CyAsDmaQueueEntry)) ;
+ if (entry_p == 0)
+ {
+ CyAsDmaStopInternal(dev_p) ;
+ return CY_AS_ERROR_OUT_OF_MEMORY ;
+ }
+ CyAsDmaAddRequestToFreeQueue(dev_p, entry_p) ;
+ }
+
+ /*
+ * Pre-allocate the DMA requests for sending EP0 and EP1 data to West Bridge
+ */
+ dev_p->usb_ep0_dma_req = CyAsLLCreateRequest(dev_p, CY_RQT_USB_EP_DATA, CY_RQT_USB_RQT_CONTEXT, 64) ;
+ dev_p->usb_ep1_dma_req = CyAsLLCreateRequest(dev_p, CY_RQT_USB_EP_DATA, CY_RQT_USB_RQT_CONTEXT, 64) ;
+ if (dev_p->usb_ep0_dma_req == 0 || dev_p->usb_ep1_dma_req == 0)
+ {
+ CyAsDmaStopInternal(dev_p) ;
+ return CY_AS_ERROR_OUT_OF_MEMORY ;
+ }
+ dev_p->usb_ep0_dma_req_save = dev_p->usb_ep0_dma_req ;
+
+ dev_p->usb_ep0_dma_resp = CyAsLLCreateResponse(dev_p, 1) ;
+ dev_p->usb_ep1_dma_resp = CyAsLLCreateResponse(dev_p, 1) ;
+ if (dev_p->usb_ep0_dma_resp == 0 || dev_p->usb_ep1_dma_resp == 0)
+ {
+ CyAsDmaStopInternal(dev_p) ;
+ return CY_AS_ERROR_OUT_OF_MEMORY ;
+ }
+ dev_p->usb_ep0_dma_resp_save = dev_p->usb_ep0_dma_resp ;
+
+ /*
+ * Set the dev_p->endp to all zeros to insure cleanup is possible if
+ * an error occurs during initialization.
+ */
+ CyAsHalMemSet(dev_p->endp, 0, sizeof(dev_p->endp)) ;
+
+ /*
+ * Now, iterate through each of the endpoints and initialize each
+ * one.
+ */
+ for(i = 0 ; i < sizeof(dev_p->endp)/sizeof(dev_p->endp[0]) ; i++)
+ {
+ dev_p->endp[i] = (CyAsDmaEndPoint *)CyAsHalAlloc(sizeof(CyAsDmaEndPoint)) ;
+ if (dev_p->endp[i] == 0)
+ {
+ CyAsDmaStopInternal(dev_p) ;
+ return CY_AS_ERROR_OUT_OF_MEMORY ;
+ }
+ CyAsHalMemSet(dev_p->endp[i], 0, sizeof(CyAsDmaEndPoint)) ;
+
+ dev_p->endp[i]->ep = i ;
+ dev_p->endp[i]->queue_p = 0 ;
+ dev_p->endp[i]->last_p = 0 ;
+
+ CyAsDmaSetDrq(dev_p, i, CyFalse) ;
+
+ if (!CyAsHalCreateSleepChannel(&dev_p->endp[i]->channel))
+ return CY_AS_ERROR_CREATE_SLEEP_CHANNEL_FAILED ;
+ }
+
+ /*
+ * Tell the HAL layer who to call when the HAL layer completes a DMA request
+ */
+ CyAsHalDmaRegisterCallback(dev_p->tag, CyAsDmaCompletedCallback) ;
+
+ /*
+ * Mark DMA as up and running on this device
+ */
+ CyAsDeviceSetDmaRunning(dev_p) ;
+
+ return CY_AS_ERROR_SUCCESS ;
+}
+
+/*
+* Wait for all entries in the DMA queue associated the given endpoint to be drained. This
+* function will not return until all the DMA data has been transferred.
+*/
+CyAsReturnStatus_t
+CyAsDmaDrainQueue(CyAsDevice *dev_p, CyAsEndPointNumber_t ep, CyBool kickstart)
+{
+ CyAsDmaEndPoint *ep_p ;
+ int loopcount = 1000 ;
+ uint32_t mask ;
+
+ /*
+ * Make sure the endpoint is valid
+ */
+ if (ep >= sizeof(dev_p->endp)/sizeof(dev_p->endp[0]))
+ return CY_AS_ERROR_INVALID_ENDPOINT ;
+
+ /* Get the endpoint pointer based on the endpoint number */
+ ep_p = CY_AS_NUM_EP(dev_p, ep) ;
+
+ /*
+ * If the endpoint is empty of traffic, we return
+ * with success immediately
+ */
+ mask = CyAsHalDisableInterrupts() ;
+ if (ep_p->queue_p == 0)
+ {
+ CyAsHalEnableInterrupts(mask) ;
+ return CY_AS_ERROR_SUCCESS ;
+ }
+ else
+ {
+ /*
+ * Add 10 seconds to the time out value for each 64 KB segment
+ * of data to be transferred.
+ */
+ if (ep_p->queue_p->size > 0x10000)
+ loopcount += ((ep_p->queue_p->size / 0x10000) * 1000) ;
+ }
+ CyAsHalEnableInterrupts(mask) ;
+
+ /* If we are already sleeping on this endpoint, it is an error */
+ if (CyAsDmaEndPointIsSleeping(ep_p))
+ return CY_AS_ERROR_NESTED_SLEEP ;
+
+ /*
+ * We disable the endpoint while the queue drains to
+ * prevent any additional requests from being queued while we are waiting
+ */
+ CyAsDmaEnableEndPoint(dev_p, ep, CyFalse, CyAsDirectionDontChange) ;
+
+ if (kickstart)
+ {
+ /*
+ * Now, kick start the DMA if necessary
+ */
+ CyAsDmaKickStart(dev_p, ep) ;
+ }
+
+ /*
+ * Check one last time before we begin sleeping to see if the
+ * queue is drained.
+ */
+ if (ep_p->queue_p == 0)
+ {
+ CyAsDmaEnableEndPoint(dev_p, ep, CyTrue, CyAsDirectionDontChange) ;
+ return CY_AS_ERROR_SUCCESS ;
+ }
+
+ while (loopcount-- > 0)
+ {
+ /*
+ * Sleep for 10 ms maximum (per loop) while waiting for the transfer
+ * to complete.
+ */
+ CyAsDmaEndPointSetSleepState(ep_p) ;
+ CyAsHalSleepOn(&ep_p->channel, 10) ;
+
+ /* If we timed out, the sleep bit will still be set */
+ CyAsDmaEndPointSetWakeState(ep_p) ;
+
+ /* Check the queue to see if is drained */
+ if (ep_p->queue_p == 0)
+ {
+ /*
+ * Clear the endpoint running and in transit flags for the endpoint,
+ * now that its DMA queue is empty.
+ */
+ CyAsDmaEndPointClearInTransit(ep_p) ;
+ CyAsDmaEndPointSetStopped(ep_p) ;
+
+ CyAsDmaEnableEndPoint(dev_p, ep, CyTrue, CyAsDirectionDontChange) ;
+ return CY_AS_ERROR_SUCCESS ;
+ }
+ }
+
+ /*
+ * The DMA operation that has timed out can be cancelled, so that later
+ * operations on this queue can proceed.
+ */
+ CyAsDmaCancel(dev_p, ep, CY_AS_ERROR_TIMEOUT) ;
+ CyAsDmaEnableEndPoint(dev_p, ep, CyTrue, CyAsDirectionDontChange) ;
+ return CY_AS_ERROR_TIMEOUT ;
+}
+
+/*
+* This function queues a write request in the DMA queue for a given endpoint. The direction of the
+* entry will be inferred from the endpoint direction.
+*/
+CyAsReturnStatus_t
+CyAsDmaQueueRequest(CyAsDevice *dev_p, CyAsEndPointNumber_t ep, void *mem_p, uint32_t size, CyBool pkt, CyBool readreq, CyAsDmaCallback cb)
+{
+ uint32_t mask ;
+ CyAsDmaQueueEntry *entry_p ;
+ CyAsDmaEndPoint *ep_p ;
+
+ /*
+ * Make sure the endpoint is valid
+ */
+ if (ep >= sizeof(dev_p->endp)/sizeof(dev_p->endp[0]))
+ return CY_AS_ERROR_INVALID_ENDPOINT ;
+
+ /* Get the endpoint pointer based on the endpoint number */
+ ep_p = CY_AS_NUM_EP(dev_p, ep) ;
+
+ if (!CyAsDmaEndPointIsEnabled(ep_p))
+ return CY_AS_ERROR_ENDPOINT_DISABLED ;
+
+ entry_p = CyAsDmaGetDmaQueueEntry(dev_p) ;
+
+ entry_p->buf_p = mem_p ;
+ entry_p->cb = cb ;
+ entry_p->size = size ;
+ entry_p->offset = 0 ;
+ entry_p->packet = pkt ;
+ entry_p->readreq = readreq ;
+
+ mask = CyAsHalDisableInterrupts() ;
+ entry_p->next_p = 0 ;
+ if (ep_p->last_p)
+ ep_p->last_p->next_p = entry_p ;
+ ep_p->last_p = entry_p ;
+ if (ep_p->queue_p == 0)
+ ep_p->queue_p = entry_p ;
+ CyAsHalEnableInterrupts(mask) ;
+
+ return CY_AS_ERROR_SUCCESS ;
+}
+
+/*
+* This function enables or disables and endpoint for DMA queueing. If an endpoint is disabled, any queued
+* requests continue to be processed, but no new requests can be queued.
+*/
+CyAsReturnStatus_t
+CyAsDmaEnableEndPoint(CyAsDevice *dev_p, CyAsEndPointNumber_t ep, CyBool enable, CyAsDmaDirection dir)
+{
+ CyAsDmaEndPoint *ep_p ;
+
+ /*
+ * Make sure the endpoint is valid
+ */
+ if (ep >= sizeof(dev_p->endp)/sizeof(dev_p->endp[0]))
+ return CY_AS_ERROR_INVALID_ENDPOINT ;
+
+ /* Get the endpoint pointer based on the endpoint number */
+ ep_p = CY_AS_NUM_EP(dev_p, ep) ;
+
+ if (dir == CyAsDirectionOut)
+ CyAsDmaEndPointSetDirectionOut(ep_p) ;
+ else if (dir == CyAsDirectionIn)
+ CyAsDmaEndPointSetDirectionIn(ep_p) ;
+
+ /*
+ * Get the maximum size of data buffer the HAL layer can accept. This is used when
+ * the DMA module is sending DMA requests to the HAL. The DMA module will never send
+ * down a request that is greater than this value.
+ *
+ * For EP0 and EP1, we can send no more than 64 bytes of data at one time as this is the
+ * maximum size of a packet that can be sent via these endpoints.
+ */
+ if (ep == 0 || ep == 1)
+ ep_p->maxhaldata = 64 ;
+ else
+ ep_p->maxhaldata = CyAsHalDmaMaxRequestSize(dev_p->tag, ep) ;
+
+ if (enable)
+ CyAsDmaEndPointEnable(ep_p) ;
+ else
+ CyAsDmaEndPointDisable(ep_p) ;
+
+ return CY_AS_ERROR_SUCCESS ;
+}
+
+/*
+ * This function cancels any DMA operations pending with the HAL layer as well
+ * as any DMA operation queued on the endpoint.
+ */
+CyAsReturnStatus_t
+CyAsDmaCancel(
+ CyAsDevice *dev_p,
+ CyAsEndPointNumber_t ep,
+ CyAsReturnStatus_t err)
+{
+ uint32_t mask ;
+ CyAsDmaEndPoint *ep_p ;
+ CyAsDmaQueueEntry *entry_p ;
+ CyBool epstate ;
+
+ /*
+ * Make sure the endpoint is valid
+ */
+ if (ep >= sizeof(dev_p->endp)/sizeof(dev_p->endp[0]))
+ return CY_AS_ERROR_INVALID_ENDPOINT ;
+
+ /* Get the endpoint pointer based on the endpoint number */
+ ep_p = CY_AS_NUM_EP(dev_p, ep) ;
+
+ if (ep_p)
+ {
+ /* Remember the state of the endpoint */
+ epstate = CyAsDmaEndPointIsEnabled(ep_p) ;
+
+ /*
+ * Disable the endpoint so no more DMA packets can be
+ * queued.
+ */
+ CyAsDmaEnableEndPoint(dev_p, ep, CyFalse, CyAsDirectionDontChange) ;
+
+ /*
+ * Don't allow any interrupts from this endpoint while we get the
+ * most current request off of the queue.
+ */
+ CyAsDmaSetDrq(dev_p, ep, CyFalse) ;
+
+ /*
+ * Cancel any pending request queued in the HAL layer
+ */
+ if (CyAsDmaEndPointInTransit(ep_p))
+ CyAsHalDmaCancelRequest(dev_p->tag, ep_p->ep) ;
+
+ /*
+ * Shutdown the DMA for this endpoint so no more data is transferred
+ */
+ CyAsDmaEndPointSetStopped(ep_p) ;
+
+ /*
+ * Mark the endpoint as not in transit, because we are going to consume
+ * any queued requests
+ */
+ CyAsDmaEndPointClearInTransit(ep_p) ;
+
+ /*
+ * Now, remove each entry in the queue and call the associated callback
+ * stating that the request was canceled.
+ */
+ ep_p->last_p = 0 ;
+ while (ep_p->queue_p != 0)
+ {
+ /* Disable interrupts to manipulate the queue */
+ mask = CyAsHalDisableInterrupts() ;
+
+ /* Remove an entry from the queue */
+ entry_p = ep_p->queue_p ;
+ ep_p->queue_p = entry_p->next_p ;
+
+ /* Ok, the queue has been updated, we can turn interrupts back on */
+ CyAsHalEnableInterrupts(mask) ;
+
+ /* Call the callback indicating we have canceled the DMA */
+ if (entry_p->cb)
+ entry_p->cb(dev_p, ep, entry_p->buf_p, entry_p->size, err) ;
+
+ CyAsDmaAddRequestToFreeQueue(dev_p, entry_p) ;
+ }
+
+ if (ep == 0 || ep == 1)
+ {
+ /*
+ * If this endpoint is zero or one, we need to clear the queue of any pending
+ * CY_RQT_USB_EP_DATA requests as these are pending requests to send data to
+ * the West Bridge device.
+ */
+ CyAsLLRemoveEpDataRequests(dev_p, ep) ;
+ }
+
+ if (epstate)
+ {
+ /*
+ * The endpoint started out enabled, so we re-enable the endpoint here.
+ */
+ CyAsDmaEnableEndPoint(dev_p, ep, CyTrue, CyAsDirectionDontChange) ;
+ }
+ }
+
+ return CY_AS_ERROR_SUCCESS ;
+}
+
+CyAsReturnStatus_t
+CyAsDmaReceivedData(CyAsDevice *dev_p, CyAsEndPointNumber_t ep, uint32_t dsize, void *data)
+{
+ CyAsDmaQueueEntry *dma_p ;
+ uint8_t *src_p, *dest_p ;
+ CyAsDmaEndPoint *ep_p ;
+ uint32_t xfersize ;
+
+ /*
+ * Make sure the endpoint is valid
+ */
+ if (ep != 0 && ep != 1)
+ return CY_AS_ERROR_INVALID_ENDPOINT ;
+
+ /* Get the endpoint pointer based on the endpoint number */
+ ep_p = CY_AS_NUM_EP(dev_p, ep) ;
+ dma_p = ep_p->queue_p ;
+ if (dma_p == 0)
+ return CY_AS_ERROR_SUCCESS ;
+
+ /*
+ * If the data received exceeds the size of the DMA buffer, clip the data to the size
+ * of the buffer. This can lead to loosing some data, but is not different than doing
+ * non-packet reads on the other endpoints.
+ */
+ if (dsize > dma_p->size - dma_p->offset)
+ dsize = dma_p->size - dma_p->offset ;
+
+ /*
+ * Copy the data from the request packet to the DMA buffer for the endpoint
+ */
+ src_p = (uint8_t *)data ;
+ dest_p = ((uint8_t *)(dma_p->buf_p)) + dma_p->offset ;
+ xfersize = dsize ;
+ while (xfersize-- > 0)
+ *dest_p++ = *src_p++ ;
+
+ /* Signal the DMA module that we have received data for this EP request */
+ CyAsDmaCompletedCallback(dev_p->tag, ep, dsize, CY_AS_ERROR_SUCCESS) ;
+
+ return CY_AS_ERROR_SUCCESS ;
+}