diff options
Diffstat (limited to 'drivers/staging/westbridge/astoria/api/src/cyasdma.c')
-rw-r--r-- | drivers/staging/westbridge/astoria/api/src/cyasdma.c | 1107 |
1 files changed, 0 insertions, 1107 deletions
diff --git a/drivers/staging/westbridge/astoria/api/src/cyasdma.c b/drivers/staging/westbridge/astoria/api/src/cyasdma.c deleted file mode 100644 index c461d4f60bfb..000000000000 --- a/drivers/staging/westbridge/astoria/api/src/cyasdma.c +++ /dev/null @@ -1,1107 +0,0 @@ -/* Cypress West Bridge API source file (cyasdma.c) -## =========================== -## Copyright (C) 2010 Cypress Semiconductor -## -## This program is free software; you can redistribute it and/or -## modify it under the terms of the GNU General Public License -## as published by the Free Software Foundation; either version 2 -## of the License, or (at your option) any later version. -## -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. -## -## You should have received a copy of the GNU General Public License -## along with this program; if not, write to the Free Software -## Foundation, Inc., 51 Franklin Street, Fifth Floor -## Boston, MA 02110-1301, USA. -## =========================== -*/ - -#include "../../include/linux/westbridge/cyashal.h" -#include "../../include/linux/westbridge/cyasdma.h" -#include "../../include/linux/westbridge/cyaslowlevel.h" -#include "../../include/linux/westbridge/cyaserr.h" -#include "../../include/linux/westbridge/cyasregs.h" - -/* - * Add the DMA queue entry to the free list to be re-used later - */ -static void -cy_as_dma_add_request_to_free_queue(cy_as_device *dev_p, - cy_as_dma_queue_entry *req_p) -{ - uint32_t imask; - imask = cy_as_hal_disable_interrupts(); - - req_p->next_p = dev_p->dma_freelist_p; - dev_p->dma_freelist_p = req_p; - - cy_as_hal_enable_interrupts(imask); -} - -/* - * Get a DMA queue entry from the free list. - */ -static cy_as_dma_queue_entry * -cy_as_dma_get_dma_queue_entry(cy_as_device *dev_p) -{ - cy_as_dma_queue_entry *req_p; - uint32_t imask; - - cy_as_hal_assert(dev_p->dma_freelist_p != 0); - - imask = cy_as_hal_disable_interrupts(); - req_p = dev_p->dma_freelist_p; - dev_p->dma_freelist_p = req_p->next_p; - cy_as_hal_enable_interrupts(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. - */ -cy_as_return_status_t -cy_as_dma_set_max_dma_size(cy_as_device *dev_p, - cy_as_end_point_number_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 -cy_as_dma_request_callback( - cy_as_device *dev_p, - uint8_t context, - cy_as_ll_request_response *req_p, - cy_as_ll_request_response *resp_p, - cy_as_return_status_t ret) -{ - uint16_t v; - uint16_t datacnt; - cy_as_end_point_number_t ep; - - (void)context; - - cy_as_log_debug_message(5, "cy_as_dma_request_callback called"); - - /* - * extract the return code from the firmware - */ - if (ret == CY_AS_ERROR_SUCCESS) { - if (cy_as_ll_request_response__get_code(resp_p) != - CY_RESP_SUCCESS_FAILURE) - ret = CY_AS_ERROR_INVALID_RESPONSE; - else - ret = cy_as_ll_request_response__get_word(resp_p, 0); - } - - /* - * extract the endpoint number and the transferred byte count - * from the request. - */ - v = cy_as_ll_request_response__get_word(req_p, 0); - ep = (cy_as_end_point_number_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) - cy_as_dma_completed_callback(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 -cy_as_dma_set_drq(cy_as_device *dev_p, - cy_as_end_point_number_t ep, cy_bool 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 = cy_as_hal_disable_interrupts(); - - /* - * set the DRQ bit to the given state for the ep given - */ - mask = (1 << ep); - v = cy_as_hal_read_register(dev_p->tag, CY_AS_MEM_P0_DRQ_MASK); - - if (state) - v |= mask; - else - v &= ~mask; - - cy_as_hal_write_register(dev_p->tag, CY_AS_MEM_P0_DRQ_MASK, v); - cy_as_hal_enable_interrupts(intval); -} - -/* -* Send the next DMA request for the endpoint given -*/ -static void -cy_as_dma_send_next_dma_request(cy_as_device *dev_p, cy_as_dma_end_point *ep_p) -{ - uint32_t datacnt; - void *buf_p; - cy_as_dma_queue_entry *dma_p; - - cy_as_log_debug_message(6, "cy_as_dma_send_next_dma_request called"); - - /* If the queue is empty, nothing to do */ - dma_p = ep_p->queue_p; - if (dma_p == 0) { - /* - * there are no 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. - */ - cy_as_dma_set_drq(dev_p, ep_p->ep, cy_false); - return; - } - - cy_as_dma_end_point_set_running(ep_p); - - /* - * get the number of words that still - * need to be xferred in this request. - */ - datacnt = dma_p->size - dma_p->offset; - cy_as_hal_assert(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. - */ - cy_as_hal_assert(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 - */ - cy_as_dma_end_point_set_in_transit(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 == cy_false) { - uint16_t v; - uint16_t len; - cy_as_ll_request_response *resp_p; - cy_as_ll_request_response *req_p; - cy_as_return_status_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; - } - - cy_as_hal_assert(req_p != 0); - cy_as_hal_assert(resp_p != 0); - cy_as_hal_assert(len <= 64); - - cy_as_ll_init_request(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 */ - - cy_as_ll_request_response__set_word(req_p, 0, v); - cy_as_ll_request_response__pack(req_p, - 1, datacnt, buf_p); - - cy_as_ll_init_response(resp_p, 1); - - ret = cy_as_ll_send_request(dev_p, req_p, resp_p, - cy_false, cy_as_dma_request_callback); - if (ret == CY_AS_ERROR_SUCCESS) - cy_as_log_debug_message(5, - "+++ send EP 0/1 data via mailbox registers"); - else - cy_as_log_debug_message(5, - "+++ error sending EP 0/1 data via mailbox " - "registers - CY_AS_ERROR_TIMEOUT"); - - if (ret != CY_AS_ERROR_SUCCESS) - cy_as_dma_completed_callback(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 (cy_as_dma_end_point_is_direction_in(ep_p)) - cy_as_hal_dma_setup_write(dev_p->tag, - ep_p->ep, buf_p, datacnt, ep_p->maxhwdata); - else - cy_as_hal_dma_setup_read(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. - */ - cy_as_dma_set_drq(dev_p, ep_p->ep, cy_true); - } -} - -/* - * 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 -cy_as_dma_completed_callback(cy_as_hal_device_tag tag, - cy_as_end_point_number_t ep, uint32_t cnt, cy_as_return_status_t status) -{ - uint32_t mask; - cy_as_dma_queue_entry *req_p; - cy_as_dma_end_point *ep_p; - cy_as_device *dev_p = cy_as_device_find_from_tag(tag); - - /* Make sure the HAL layer gave us good parameters */ - cy_as_hal_assert(dev_p != 0); - cy_as_hal_assert(dev_p->sig == CY_AS_DEVICE_HANDLE_SIGNATURE); - cy_as_hal_assert(ep < 16); - - - /* Get the endpoint ptr */ - ep_p = CY_AS_NUM_EP(dev_p, ep); - cy_as_hal_assert(ep_p->queue_p != 0); - - /* Get a pointer to the current entry in the queue */ - mask = cy_as_hal_disable_interrupts(); - 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 == cy_false) && - ((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))) { - cy_as_hal_enable_interrupts(mask); - - /* - * and send the request again to send the next block of - * data. special handling for MTP transfers on E_ps 2 - * and 6. the send_next_request will be processed based - * on the event sent by the firmware. - */ - if ((ep == CY_AS_MTP_WRITE_ENDPOINT) || ( - (ep == CY_AS_MTP_READ_ENDPOINT) && - (!cy_as_dma_end_point_is_direction_in(ep_p)))) - cy_as_dma_end_point_set_stopped(ep_p); - else - cy_as_dma_send_next_dma_request(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. - */ - cy_as_dma_end_point_clear_in_transit(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; - cy_as_hal_enable_interrupts(mask); - cy_as_dma_set_drq(dev_p, ep, cy_false); - } else - cy_as_hal_enable_interrupts(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 in_callback bit to insure that we - * cannot recursively call an API function that is - * synchronous only from a callback. - */ - cy_as_device_set_in_callback(dev_p); - (*req_p->cb)(dev_p, ep, req_p->buf_p, - req_p->offset, status); - cy_as_device_clear_in_callback(dev_p); - } - - /* - * we are done with this request, put it on the freelist to be - * reused at a later time. - */ - cy_as_dma_add_request_to_free_queue(dev_p, req_p); - - if (ep_p->queue_p == 0) { - /* - * if the endpoint is out of DMA entries, set the - * endpoint as stopped. - */ - cy_as_dma_end_point_set_stopped(ep_p); - - /* - * the DMA queue is empty, wake any task waiting on - * the QUEUE to drain. - */ - if (cy_as_dma_end_point_is_sleeping(ep_p)) { - cy_as_dma_end_point_set_wake_state(ep_p); - cy_as_hal_wake(&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) && - (!cy_as_dma_end_point_is_direction_in(ep_p))) || - ((ep == dev_p->storage_read_endpoint) && - (!cy_as_device_is_p2s_dma_start_recvd(dev_p))) - || ((ep == dev_p->storage_write_endpoint) && - (!cy_as_device_is_p2s_dma_start_recvd(dev_p)))) - cy_as_dma_end_point_set_stopped(ep_p); - else - cy_as_dma_send_next_dma_request(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. -*/ -cy_as_return_status_t -cy_as_dma_kick_start(cy_as_device *dev_p, cy_as_end_point_number_t ep) -{ - cy_as_dma_end_point *ep_p; - cy_as_hal_assert(dev_p->sig == CY_AS_DEVICE_HANDLE_SIGNATURE); - - ep_p = CY_AS_NUM_EP(dev_p, ep); - - /* We are already running */ - if (cy_as_dma_end_point_is_running(ep_p)) - return CY_AS_ERROR_SUCCESS; - - cy_as_dma_send_next_dma_request(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 cy_as_return_status_t -cy_as_dma_stop_end_point(cy_as_device *dev_p, cy_as_end_point_number_t ep) -{ - cy_as_return_status_t ret; - cy_as_dma_end_point *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 = cy_as_dma_cancel(dev_p, ep, CY_AS_ERROR_CANCELED); - if (ret != CY_AS_ERROR_SUCCESS) - return ret; - - /* - * destroy the sleep channel - */ - if (!cy_as_hal_destroy_sleep_channel(&ep_p->channel) - && ret == CY_AS_ERROR_SUCCESS) - ret = CY_AS_ERROR_DESTROY_SLEEP_CHANNEL_FAILED; - - /* - * free the memory associated with this endpoint - */ - cy_as_hal_free(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 cy_as_return_status_t -cy_as_dma_stop_internal(cy_as_device *dev_p) -{ - cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; - cy_as_return_status_t lret; - cy_as_end_point_number_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 = cy_as_dma_stop_end_point(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) { - cy_as_dma_queue_entry *req_p; - uint32_t imask = cy_as_hal_disable_interrupts(); - - req_p = dev_p->dma_freelist_p; - dev_p->dma_freelist_p = req_p->next_p; - - cy_as_hal_enable_interrupts(imask); - - cy_as_hal_free(req_p); - } - - cy_as_ll_destroy_request(dev_p, dev_p->usb_ep0_dma_req); - cy_as_ll_destroy_request(dev_p, dev_p->usb_ep1_dma_req); - cy_as_ll_destroy_response(dev_p, dev_p->usb_ep0_dma_resp); - cy_as_ll_destroy_response(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. - */ -cy_as_return_status_t -cy_as_dma_stop(cy_as_device *dev_p) -{ - cy_as_return_status_t ret; - - ret = cy_as_dma_stop_internal(dev_p); - cy_as_device_set_dma_stopped(dev_p); - - return ret; -} - -/* - * CyAsDmaStart() - * - * This function initializes the DMA module to insure it is up and running. - */ -cy_as_return_status_t -cy_as_dma_start(cy_as_device *dev_p) -{ - cy_as_end_point_number_t i; - uint16_t cnt; - - if (cy_as_device_is_dma_running(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++) { - cy_as_dma_queue_entry *entry_p = (cy_as_dma_queue_entry *) - cy_as_hal_alloc(sizeof(cy_as_dma_queue_entry)); - if (entry_p == 0) { - cy_as_dma_stop_internal(dev_p); - return CY_AS_ERROR_OUT_OF_MEMORY; - } - cy_as_dma_add_request_to_free_queue(dev_p, entry_p); - } - - /* - * pre-allocate the DMA requests for sending EP0 - * and EP1 data to west bridge - */ - dev_p->usb_ep0_dma_req = cy_as_ll_create_request(dev_p, - CY_RQT_USB_EP_DATA, CY_RQT_USB_RQT_CONTEXT, 64); - dev_p->usb_ep1_dma_req = cy_as_ll_create_request(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) { - cy_as_dma_stop_internal(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 = cy_as_ll_create_response(dev_p, 1); - dev_p->usb_ep1_dma_resp = cy_as_ll_create_response(dev_p, 1); - if (dev_p->usb_ep0_dma_resp == 0 || dev_p->usb_ep1_dma_resp == 0) { - cy_as_dma_stop_internal(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. - */ - cy_as_hal_mem_set(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] = (cy_as_dma_end_point *) - cy_as_hal_alloc(sizeof(cy_as_dma_end_point)); - if (dev_p->endp[i] == 0) { - cy_as_dma_stop_internal(dev_p); - return CY_AS_ERROR_OUT_OF_MEMORY; - } - cy_as_hal_mem_set(dev_p->endp[i], 0, - sizeof(cy_as_dma_end_point)); - - dev_p->endp[i]->ep = i; - dev_p->endp[i]->queue_p = 0; - dev_p->endp[i]->last_p = 0; - - cy_as_dma_set_drq(dev_p, i, cy_false); - - if (!cy_as_hal_create_sleep_channel(&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 - */ - cy_as_hal_dma_register_callback(dev_p->tag, - cy_as_dma_completed_callback); - - /* - * mark DMA as up and running on this device - */ - cy_as_device_set_dma_running(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. -*/ -cy_as_return_status_t -cy_as_dma_drain_queue(cy_as_device *dev_p, - cy_as_end_point_number_t ep, cy_bool kickstart) -{ - cy_as_dma_end_point *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 = cy_as_hal_disable_interrupts(); - if (ep_p->queue_p == 0) { - cy_as_hal_enable_interrupts(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); - } - cy_as_hal_enable_interrupts(mask); - - /* If we are already sleeping on this endpoint, it is an error */ - if (cy_as_dma_end_point_is_sleeping(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 - */ - cy_as_dma_enable_end_point(dev_p, ep, - cy_false, cy_as_direction_dont_change); - - if (kickstart) { - /* - * now, kick start the DMA if necessary - */ - cy_as_dma_kick_start(dev_p, ep); - } - - /* - * check one last time before we begin sleeping to see if the - * queue is drained. - */ - if (ep_p->queue_p == 0) { - cy_as_dma_enable_end_point(dev_p, ep, cy_true, - cy_as_direction_dont_change); - return CY_AS_ERROR_SUCCESS; - } - - while (loopcount-- > 0) { - /* - * sleep for 10 ms maximum (per loop) while - * waiting for the transfer to complete. - */ - cy_as_dma_end_point_set_sleep_state(ep_p); - cy_as_hal_sleep_on(&ep_p->channel, 10); - - /* If we timed out, the sleep bit will still be set */ - cy_as_dma_end_point_set_wake_state(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. - */ - cy_as_dma_end_point_clear_in_transit(ep_p); - cy_as_dma_end_point_set_stopped(ep_p); - - cy_as_dma_enable_end_point(dev_p, ep, - cy_true, cy_as_direction_dont_change); - return CY_AS_ERROR_SUCCESS; - } - } - - /* - * the DMA operation that has timed out can be cancelled, so that later - * operations on this queue can proceed. - */ - cy_as_dma_cancel(dev_p, ep, CY_AS_ERROR_TIMEOUT); - cy_as_dma_enable_end_point(dev_p, ep, - cy_true, cy_as_direction_dont_change); - 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. -*/ -cy_as_return_status_t -cy_as_dma_queue_request(cy_as_device *dev_p, - cy_as_end_point_number_t ep, void *mem_p, - uint32_t size, cy_bool pkt, cy_bool readreq, cy_as_dma_callback cb) -{ - uint32_t mask; - cy_as_dma_queue_entry *entry_p; - cy_as_dma_end_point *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 (!cy_as_dma_end_point_is_enabled(ep_p)) - return CY_AS_ERROR_ENDPOINT_DISABLED; - - entry_p = cy_as_dma_get_dma_queue_entry(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 = cy_as_hal_disable_interrupts(); - 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; - cy_as_hal_enable_interrupts(mask); - - return CY_AS_ERROR_SUCCESS; -} - -/* -* This function enables or disables and endpoint for DMA -* queueing. If an endpoint is disabled, any queue requests -* continue to be processed, but no new requests can be queued. -*/ -cy_as_return_status_t -cy_as_dma_enable_end_point(cy_as_device *dev_p, - cy_as_end_point_number_t ep, cy_bool enable, cy_as_dma_direction dir) -{ - cy_as_dma_end_point *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 == cy_as_direction_out) - cy_as_dma_end_point_set_direction_out(ep_p); - else if (dir == cy_as_direction_in) - cy_as_dma_end_point_set_direction_in(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 = cy_as_hal_dma_max_request_size( - dev_p->tag, ep); - - if (enable) - cy_as_dma_end_point_enable(ep_p); - else - cy_as_dma_end_point_disable(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. - */ -cy_as_return_status_t -cy_as_dma_cancel( - cy_as_device *dev_p, - cy_as_end_point_number_t ep, - cy_as_return_status_t err) -{ - uint32_t mask; - cy_as_dma_end_point *ep_p; - cy_as_dma_queue_entry *entry_p; - cy_bool 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 = cy_as_dma_end_point_is_enabled(ep_p); - - /* - * disable the endpoint so no more DMA packets can be - * queued. - */ - cy_as_dma_enable_end_point(dev_p, ep, - cy_false, cy_as_direction_dont_change); - - /* - * don't allow any interrupts from this endpoint - * while we get the most current request off of - * the queue. - */ - cy_as_dma_set_drq(dev_p, ep, cy_false); - - /* - * cancel any pending request queued in the HAL layer - */ - if (cy_as_dma_end_point_in_transit(ep_p)) - cy_as_hal_dma_cancel_request(dev_p->tag, ep_p->ep); - - /* - * shutdown the DMA for this endpoint so no - * more data is transferred - */ - cy_as_dma_end_point_set_stopped(ep_p); - - /* - * mark the endpoint as not in transit, because we are - * going to consume any queued requests - */ - cy_as_dma_end_point_clear_in_transit(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 = cy_as_hal_disable_interrupts(); - - /* 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 */ - cy_as_hal_enable_interrupts(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); - - cy_as_dma_add_request_to_free_queue(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. - */ - cy_as_ll_remove_ep_data_requests(dev_p, ep); - } - - if (epstate) { - /* - * the endpoint started out enabled, so we - * re-enable the endpoint here. - */ - cy_as_dma_enable_end_point(dev_p, ep, - cy_true, cy_as_direction_dont_change); - } - } - - return CY_AS_ERROR_SUCCESS; -} - -cy_as_return_status_t -cy_as_dma_received_data(cy_as_device *dev_p, - cy_as_end_point_number_t ep, uint32_t dsize, void *data) -{ - cy_as_dma_queue_entry *dma_p; - uint8_t *src_p, *dest_p; - cy_as_dma_end_point *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 losing 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 */ - cy_as_dma_completed_callback(dev_p->tag, - ep, dsize, CY_AS_ERROR_SUCCESS); - - return CY_AS_ERROR_SUCCESS; -} |