]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/staging/westbridge/astoria/api/src/cyaslowlevel.c
Merge branch 'master' into csb1725
[mv-sheeva.git] / drivers / staging / westbridge / astoria / api / src / cyaslowlevel.c
diff --git a/drivers/staging/westbridge/astoria/api/src/cyaslowlevel.c b/drivers/staging/westbridge/astoria/api/src/cyaslowlevel.c
new file mode 100644 (file)
index 0000000..d43dd85
--- /dev/null
@@ -0,0 +1,1264 @@
+/* Cypress West Bridge API source file (cyaslowlevel.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/cyascast.h"
+#include "../../include/linux/westbridge/cyasdevice.h"
+#include "../../include/linux/westbridge/cyaslowlevel.h"
+#include "../../include/linux/westbridge/cyasintr.h"
+#include "../../include/linux/westbridge/cyaserr.h"
+#include "../../include/linux/westbridge/cyasregs.h"
+
+static const uint32_t cy_as_low_level_timeout_count = 65536 * 4;
+
+/* Forward declaration */
+static cy_as_return_status_t cy_as_send_one(cy_as_device *dev_p,
+       cy_as_ll_request_response *req_p);
+
+/*
+* This array holds the size of the largest request we will ever recevie from
+* the West Bridge device per context.  The size is in 16 bit words.  Note a
+* size of 0xffff indicates that there will be no requests on this context
+* from West Bridge.
+*/
+static uint16_t max_request_length[CY_RQT_CONTEXT_COUNT] = {
+       8, /* CY_RQT_GENERAL_RQT_CONTEXT - CY_RQT_INITIALIZATION_COMPLETE */
+       8, /* CY_RQT_RESOURCE_RQT_CONTEXT - none */
+       8, /* CY_RQT_STORAGE_RQT_CONTEXT - CY_RQT_MEDIA_CHANGED */
+       128, /* CY_RQT_USB_RQT_CONTEXT - CY_RQT_USB_EVENT */
+       8 /* CY_RQT_TUR_RQT_CONTEXT - CY_RQT_TURBO_CMD_FROM_HOST */
+};
+
+/*
+* For the given context, this function removes the request node at the head
+* of the queue from the context.  This is called after all processing has
+* occurred on the given request and response and we are ready to remove this
+* entry from the queue.
+*/
+static void
+cy_as_ll_remove_request_queue_head(cy_as_device *dev_p, cy_as_context *ctxt_p)
+{
+       uint32_t mask, state;
+       cy_as_ll_request_list_node *node_p;
+
+       (void)dev_p;
+       cy_as_hal_assert(ctxt_p->request_queue_p != 0);
+
+       mask = cy_as_hal_disable_interrupts();
+       node_p = ctxt_p->request_queue_p;
+       ctxt_p->request_queue_p = node_p->next;
+       cy_as_hal_enable_interrupts(mask);
+
+       node_p->callback = 0;
+       node_p->rqt = 0;
+       node_p->resp = 0;
+
+       /*
+       * note that the caller allocates and destroys the request and
+       * response.  generally the destroy happens in the callback for
+       * async requests and after the wait returns for sync.  the
+       * request and response may not actually be destroyed but may be
+       * managed in other ways as well.  it is the responsibilty of
+       * the caller to deal with these in any case.  the caller can do
+       * this in the request/response callback function.
+       */
+       state = cy_as_hal_disable_interrupts();
+       cy_as_hal_c_b_free(node_p);
+       cy_as_hal_enable_interrupts(state);
+}
+
+/*
+* For the context given, this function sends the next request to
+* West Bridge via the mailbox register, if the next request is
+* ready to be sent and has not already been sent.
+*/
+static void
+cy_as_ll_send_next_request(cy_as_device *dev_p, cy_as_context *ctxt_p)
+{
+       cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS;
+
+       /*
+       * ret == ret is equivalent to while (1) but eliminates compiler
+       * warnings for some compilers.
+       */
+       while (ret == ret) {
+               cy_as_ll_request_list_node *node_p = ctxt_p->request_queue_p;
+               if (node_p == 0)
+                       break;
+
+               if (cy_as_request_get_node_state(node_p) !=
+                       CY_AS_REQUEST_LIST_STATE_QUEUED)
+                       break;
+
+               cy_as_request_set_node_state(node_p,
+                       CY_AS_REQUEST_LIST_STATE_WAITING);
+               ret = cy_as_send_one(dev_p, node_p->rqt);
+               if (ret == CY_AS_ERROR_SUCCESS)
+                       break;
+
+               /*
+               * if an error occurs in sending the request, tell the requester
+               * about the error and remove the request from the queue.
+               */
+               cy_as_request_set_node_state(node_p,
+                       CY_AS_REQUEST_LIST_STATE_RECEIVED);
+               node_p->callback(dev_p, ctxt_p->number,
+                       node_p->rqt, node_p->resp, ret);
+               cy_as_ll_remove_request_queue_head(dev_p, ctxt_p);
+
+               /*
+               * this falls through to the while loop to send the next request
+               * since the previous request did not get sent.
+               */
+       }
+}
+
+/*
+* This method removes an entry from the request queue of a given context.
+* The entry is removed only if it is not in transit.
+*/
+cy_as_remove_request_result_t
+cy_as_ll_remove_request(cy_as_device *dev_p, cy_as_context *ctxt_p,
+       cy_as_ll_request_response *req_p, cy_bool force)
+{
+       uint32_t imask;
+       cy_as_ll_request_list_node *node_p;
+       cy_as_ll_request_list_node *tmp_p;
+       uint32_t state;
+
+       imask = cy_as_hal_disable_interrupts();
+       if (ctxt_p->request_queue_p != 0 &&
+               ctxt_p->request_queue_p->rqt == req_p) {
+               node_p = ctxt_p->request_queue_p;
+               if ((cy_as_request_get_node_state(node_p) ==
+                       CY_AS_REQUEST_LIST_STATE_WAITING) && (!force)) {
+                       cy_as_hal_enable_interrupts(imask);
+                       return cy_as_remove_request_in_transit;
+               }
+
+               ctxt_p->request_queue_p = node_p->next;
+       } else {
+               tmp_p = ctxt_p->request_queue_p;
+               while (tmp_p != 0 && tmp_p->next != 0 &&
+                       tmp_p->next->rqt != req_p)
+                       tmp_p = tmp_p->next;
+
+               if (tmp_p == 0 || tmp_p->next == 0) {
+                       cy_as_hal_enable_interrupts(imask);
+                       return cy_as_remove_request_not_found;
+               }
+
+               node_p = tmp_p->next;
+               tmp_p->next = node_p->next;
+       }
+
+       if (node_p->callback)
+               node_p->callback(dev_p, ctxt_p->number, node_p->rqt,
+                       node_p->resp, CY_AS_ERROR_CANCELED);
+
+       state = cy_as_hal_disable_interrupts();
+       cy_as_hal_c_b_free(node_p);
+       cy_as_hal_enable_interrupts(state);
+
+       cy_as_hal_enable_interrupts(imask);
+       return cy_as_remove_request_sucessful;
+}
+
+void
+cy_as_ll_remove_all_requests(cy_as_device *dev_p, cy_as_context *ctxt_p)
+{
+       cy_as_ll_request_list_node *node = ctxt_p->request_queue_p;
+
+       while (node) {
+               if (cy_as_request_get_node_state(ctxt_p->request_queue_p) !=
+                       CY_AS_REQUEST_LIST_STATE_RECEIVED)
+                       cy_as_ll_remove_request(dev_p, ctxt_p,
+                               node->rqt, cy_true);
+               node = node->next;
+       }
+}
+
+static cy_bool
+cy_as_ll_is_in_queue(cy_as_context *ctxt_p, cy_as_ll_request_response *req_p)
+{
+       uint32_t mask;
+       cy_as_ll_request_list_node *node_p;
+
+       mask = cy_as_hal_disable_interrupts();
+       node_p = ctxt_p->request_queue_p;
+       while (node_p) {
+               if (node_p->rqt == req_p) {
+                       cy_as_hal_enable_interrupts(mask);
+                       return cy_true;
+               }
+               node_p = node_p->next;
+       }
+       cy_as_hal_enable_interrupts(mask);
+       return cy_false;
+}
+
+/*
+* This is the handler for mailbox data when we are trying to send data
+* to the West Bridge firmware.  The firmware may be trying to send us
+* data and we need to queue this data to allow the firmware to move
+* forward and be in a state to receive our request.  Here we just queue
+* the data and it is processed at a later time by the mailbox interrupt
+* handler.
+*/
+void
+cy_as_ll_queue_mailbox_data(cy_as_device *dev_p)
+{
+       cy_as_context *ctxt_p;
+       uint8_t context;
+       uint16_t data[4];
+       int32_t i;
+
+       /* Read the data from mailbox 0 to determine what to do with the data */
+       for (i = 3; i >= 0; i--)
+               data[i] = cy_as_hal_read_register(dev_p->tag,
+                       cy_cast_int2U_int16(CY_AS_MEM_P0_MAILBOX0 + i));
+
+       context = cy_as_mbox_get_context(data[0]);
+       if (context >= CY_RQT_CONTEXT_COUNT) {
+               cy_as_hal_print_message("mailbox request/response received "
+                       "with invalid context value (%d)\n", context);
+               return;
+       }
+
+       ctxt_p = dev_p->context[context];
+
+       /*
+       * if we have queued too much data, drop future data.
+       */
+       cy_as_hal_assert(ctxt_p->queue_index * sizeof(uint16_t) +
+               sizeof(data) <= sizeof(ctxt_p->data_queue));
+
+       for (i = 0; i < 4; i++)
+               ctxt_p->data_queue[ctxt_p->queue_index++] = data[i];
+
+       cy_as_hal_assert((ctxt_p->queue_index % 4) == 0);
+       dev_p->ll_queued_data = cy_true;
+}
+
+void
+cy_as_mail_box_process_data(cy_as_device *dev_p, uint16_t *data)
+{
+       cy_as_context *ctxt_p;
+       uint8_t context;
+       uint16_t *len_p;
+       cy_as_ll_request_response *rec_p;
+       uint8_t st;
+       uint16_t src, dest;
+
+       context = cy_as_mbox_get_context(data[0]);
+       if (context >= CY_RQT_CONTEXT_COUNT) {
+               cy_as_hal_print_message("mailbox request/response received "
+               "with invalid context value (%d)\n", context);
+               return;
+       }
+
+       ctxt_p = dev_p->context[context];
+
+       if (cy_as_mbox_is_request(data[0])) {
+               cy_as_hal_assert(ctxt_p->req_p != 0);
+               rec_p = ctxt_p->req_p;
+               len_p = &ctxt_p->request_length;
+
+       } else {
+               if (ctxt_p->request_queue_p == 0 ||
+                       cy_as_request_get_node_state(ctxt_p->request_queue_p)
+                       != CY_AS_REQUEST_LIST_STATE_WAITING) {
+                       cy_as_hal_print_message("mailbox response received on "
+                               "context that was not expecting a response\n");
+                       cy_as_hal_print_message("  context: %d\n", context);
+                       cy_as_hal_print_message("  contents: 0x%04x 0x%04x "
+                               "0x%04x 0x%04x\n",
+                               data[0], data[1], data[2], data[3]);
+                       if (ctxt_p->request_queue_p != 0)
+                               cy_as_hal_print_message("  state: 0x%02x\n",
+                                       ctxt_p->request_queue_p->state);
+                       return;
+               }
+
+               /* Make sure the request has an associated response */
+               cy_as_hal_assert(ctxt_p->request_queue_p->resp != 0);
+
+               rec_p = ctxt_p->request_queue_p->resp;
+               len_p = &ctxt_p->request_queue_p->length;
+       }
+
+       if (rec_p->stored == 0) {
+               /*
+               * this is the first cycle of the response
+               */
+               cy_as_ll_request_response__set_code(rec_p,
+                       cy_as_mbox_get_code(data[0]));
+               cy_as_ll_request_response__set_context(rec_p, context);
+
+               if (cy_as_mbox_is_last(data[0])) {
+                       /* This is a single cycle response */
+                       *len_p = rec_p->length;
+                       st = 1;
+               } else {
+                       /* Ensure that enough memory has been
+                        * reserved for the response. */
+                       cy_as_hal_assert(rec_p->length >= data[1]);
+                       *len_p = (data[1] < rec_p->length) ?
+                               data[1] : rec_p->length;
+                       st = 2;
+               }
+       } else
+               st = 1;
+
+       /* Trasnfer the data from the mailboxes to the response */
+       while (rec_p->stored < *len_p && st < 4)
+               rec_p->data[rec_p->stored++] = data[st++];
+
+       if (cy_as_mbox_is_last(data[0])) {
+               /* NB: The call-back that is made below can cause the
+                * addition of more data in this queue, thus causing
+                * a recursive overflow of the queue. this is prevented
+                * by removing the request entry that is currently
+                * being passed up from the data queue. if this is done,
+                * the queue only needs to be as long as two request
+                * entries from west bridge.
+               */
+               if ((ctxt_p->rqt_index > 0) &&
+                       (ctxt_p->rqt_index <= ctxt_p->queue_index)) {
+                       dest = 0;
+                       src  = ctxt_p->rqt_index;
+
+                       while (src < ctxt_p->queue_index)
+                               ctxt_p->data_queue[dest++] =
+                                       ctxt_p->data_queue[src++];
+
+                       ctxt_p->rqt_index = 0;
+                       ctxt_p->queue_index = dest;
+                       cy_as_hal_assert((ctxt_p->queue_index % 4) == 0);
+               }
+
+               if (ctxt_p->request_queue_p != 0 && rec_p ==
+                       ctxt_p->request_queue_p->resp) {
+                       /*
+                       * if this is the last cycle of the response, call the
+                       * callback and reset for the next response.
+                       */
+                       cy_as_ll_request_response *resp_p =
+                               ctxt_p->request_queue_p->resp;
+                       resp_p->length = ctxt_p->request_queue_p->length;
+                       cy_as_request_set_node_state(ctxt_p->request_queue_p,
+                               CY_AS_REQUEST_LIST_STATE_RECEIVED);
+
+                       cy_as_device_set_in_callback(dev_p);
+                       ctxt_p->request_queue_p->callback(dev_p, context,
+                               ctxt_p->request_queue_p->rqt,
+                               resp_p, CY_AS_ERROR_SUCCESS);
+
+                       cy_as_device_clear_in_callback(dev_p);
+
+                       cy_as_ll_remove_request_queue_head(dev_p, ctxt_p);
+                       cy_as_ll_send_next_request(dev_p, ctxt_p);
+               } else {
+                       /* Send the request to the appropriate
+                        * module to handle */
+                       cy_as_ll_request_response *request_p = ctxt_p->req_p;
+                       ctxt_p->req_p = 0;
+                       if (ctxt_p->request_callback) {
+                               cy_as_device_set_in_callback(dev_p);
+                               ctxt_p->request_callback(dev_p, context,
+                                       request_p, 0, CY_AS_ERROR_SUCCESS);
+                               cy_as_device_clear_in_callback(dev_p);
+                       }
+                       cy_as_ll_init_request(request_p, 0,
+                               context, request_p->length);
+                       ctxt_p->req_p = request_p;
+               }
+       }
+}
+
+/*
+* This is the handler for processing queued mailbox data
+*/
+void
+cy_as_mail_box_queued_data_handler(cy_as_device *dev_p)
+{
+       uint16_t i;
+
+       /*
+        * if more data gets queued in between our entering this call
+        * and the end of the iteration on all contexts; we should
+        * continue processing the queued data.
+        */
+       while (dev_p->ll_queued_data) {
+               dev_p->ll_queued_data = cy_false;
+               for (i = 0; i < CY_RQT_CONTEXT_COUNT; i++) {
+                       uint16_t offset;
+                       cy_as_context *ctxt_p = dev_p->context[i];
+                       cy_as_hal_assert((ctxt_p->queue_index % 4) == 0);
+
+                       offset = 0;
+                       while (offset < ctxt_p->queue_index) {
+                               ctxt_p->rqt_index = offset + 4;
+                               cy_as_mail_box_process_data(dev_p,
+                                       ctxt_p->data_queue + offset);
+                               offset = ctxt_p->rqt_index;
+                       }
+                       ctxt_p->queue_index = 0;
+               }
+       }
+}
+
+/*
+* This is the handler for the mailbox interrupt.  This function reads
+* data from the mailbox registers until a complete request or response
+* is received.  When a complete request is received, the callback
+* associated with requests on that context is called.  When a complete
+* response is recevied, the callback associated with the request that
+* generated the reponse is called.
+*/
+void
+cy_as_mail_box_interrupt_handler(cy_as_device *dev_p)
+{
+       cy_as_hal_assert(dev_p->sig == CY_AS_DEVICE_HANDLE_SIGNATURE);
+
+       /*
+       * queue the mailbox data to preserve
+       * order for later processing.
+       */
+       cy_as_ll_queue_mailbox_data(dev_p);
+
+       /*
+       * process what was queued and anything that may be pending
+       */
+       cy_as_mail_box_queued_data_handler(dev_p);
+}
+
+cy_as_return_status_t
+cy_as_ll_start(cy_as_device *dev_p)
+{
+       uint16_t i;
+
+       if (cy_as_device_is_low_level_running(dev_p))
+               return CY_AS_ERROR_ALREADY_RUNNING;
+
+       dev_p->ll_sending_rqt = cy_false;
+       dev_p->ll_abort_curr_rqt = cy_false;
+
+       for (i = 0; i < CY_RQT_CONTEXT_COUNT; i++) {
+               dev_p->context[i] = (cy_as_context *)
+                       cy_as_hal_alloc(sizeof(cy_as_context));
+               if (dev_p->context[i] == 0)
+                       return CY_AS_ERROR_OUT_OF_MEMORY;
+
+               dev_p->context[i]->number = (uint8_t)i;
+               dev_p->context[i]->request_callback = 0;
+               dev_p->context[i]->request_queue_p = 0;
+               dev_p->context[i]->last_node_p = 0;
+               dev_p->context[i]->req_p = cy_as_ll_create_request(dev_p,
+                       0, (uint8_t)i, max_request_length[i]);
+               dev_p->context[i]->queue_index = 0;
+
+               if (!cy_as_hal_create_sleep_channel
+                       (&dev_p->context[i]->channel))
+                       return CY_AS_ERROR_CREATE_SLEEP_CHANNEL_FAILED;
+       }
+
+       cy_as_device_set_low_level_running(dev_p);
+       return CY_AS_ERROR_SUCCESS;
+}
+
+/*
+* Shutdown the low level communications module.  This operation will
+* also cancel any queued low level requests.
+*/
+cy_as_return_status_t
+cy_as_ll_stop(cy_as_device *dev_p)
+{
+       uint8_t i;
+       cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS;
+       cy_as_context *ctxt_p;
+       uint32_t mask;
+
+       for (i = 0; i < CY_RQT_CONTEXT_COUNT; i++) {
+               ctxt_p = dev_p->context[i];
+               if (!cy_as_hal_destroy_sleep_channel(&ctxt_p->channel))
+                       return CY_AS_ERROR_DESTROY_SLEEP_CHANNEL_FAILED;
+
+               /*
+               * now, free any queued requests and assocaited responses
+               */
+               while (ctxt_p->request_queue_p) {
+                       uint32_t state;
+                       cy_as_ll_request_list_node *node_p =
+                               ctxt_p->request_queue_p;
+
+                       /* Mark this pair as in a cancel operation */
+                       cy_as_request_set_node_state(node_p,
+                               CY_AS_REQUEST_LIST_STATE_CANCELING);
+
+                       /* Tell the caller that we are canceling this request */
+                       /* NB: The callback is responsible for destroying the
+                        * request and the response.  we cannot count on the
+                        * contents of these two after calling the callback.
+                       */
+                       node_p->callback(dev_p, i, node_p->rqt,
+                               node_p->resp, CY_AS_ERROR_CANCELED);
+
+                       /* Remove the pair from the queue */
+                       mask = cy_as_hal_disable_interrupts();
+                       ctxt_p->request_queue_p = node_p->next;
+                       cy_as_hal_enable_interrupts(mask);
+
+                       /* Free the list node */
+                       state = cy_as_hal_disable_interrupts();
+                       cy_as_hal_c_b_free(node_p);
+                       cy_as_hal_enable_interrupts(state);
+               }
+
+               cy_as_ll_destroy_request(dev_p, dev_p->context[i]->req_p);
+               cy_as_hal_free(dev_p->context[i]);
+               dev_p->context[i] = 0;
+
+       }
+       cy_as_device_set_low_level_stopped(dev_p);
+
+       return ret;
+}
+
+void
+cy_as_ll_init_request(cy_as_ll_request_response *req_p,
+       uint16_t code, uint16_t context, uint16_t length)
+{
+       uint16_t totallen = sizeof(cy_as_ll_request_response) +
+               (length - 1) * sizeof(uint16_t);
+
+       cy_as_hal_mem_set(req_p, 0, totallen);
+       req_p->length = length;
+       cy_as_ll_request_response__set_code(req_p, code);
+       cy_as_ll_request_response__set_context(req_p, context);
+       cy_as_ll_request_response__set_request(req_p);
+}
+
+/*
+* Create a new request.
+*/
+cy_as_ll_request_response *
+cy_as_ll_create_request(cy_as_device *dev_p, uint16_t code,
+       uint8_t context, uint16_t length)
+{
+       cy_as_ll_request_response *req_p;
+       uint32_t state;
+       uint16_t totallen = sizeof(cy_as_ll_request_response) +
+               (length - 1) * sizeof(uint16_t);
+
+       (void)dev_p;
+
+       state = cy_as_hal_disable_interrupts();
+       req_p = cy_as_hal_c_b_alloc(totallen);
+       cy_as_hal_enable_interrupts(state);
+       if (req_p)
+               cy_as_ll_init_request(req_p, code, context, length);
+
+       return req_p;
+}
+
+/*
+* Destroy a request.
+*/
+void
+cy_as_ll_destroy_request(cy_as_device *dev_p, cy_as_ll_request_response *req_p)
+{
+       uint32_t state;
+       (void)dev_p;
+       (void)req_p;
+
+       state = cy_as_hal_disable_interrupts();
+       cy_as_hal_c_b_free(req_p);
+       cy_as_hal_enable_interrupts(state);
+
+}
+
+void
+cy_as_ll_init_response(cy_as_ll_request_response *req_p, uint16_t length)
+{
+       uint16_t totallen = sizeof(cy_as_ll_request_response) +
+               (length - 1) * sizeof(uint16_t);
+
+       cy_as_hal_mem_set(req_p, 0, totallen);
+       req_p->length = length;
+       cy_as_ll_request_response__set_response(req_p);
+}
+
+/*
+* Create a new response
+*/
+cy_as_ll_request_response *
+cy_as_ll_create_response(cy_as_device *dev_p, uint16_t length)
+{
+       cy_as_ll_request_response *req_p;
+       uint32_t state;
+       uint16_t totallen = sizeof(cy_as_ll_request_response) +
+               (length - 1) * sizeof(uint16_t);
+
+       (void)dev_p;
+
+       state = cy_as_hal_disable_interrupts();
+       req_p = cy_as_hal_c_b_alloc(totallen);
+       cy_as_hal_enable_interrupts(state);
+       if (req_p)
+               cy_as_ll_init_response(req_p, length);
+
+       return req_p;
+}
+
+/*
+* Destroy the new response
+*/
+void
+cy_as_ll_destroy_response(cy_as_device *dev_p, cy_as_ll_request_response *req_p)
+{
+       uint32_t state;
+       (void)dev_p;
+       (void)req_p;
+
+       state = cy_as_hal_disable_interrupts();
+       cy_as_hal_c_b_free(req_p);
+       cy_as_hal_enable_interrupts(state);
+}
+
+static uint16_t
+cy_as_read_intr_status(
+                                  cy_as_device *dev_p)
+{
+       uint32_t mask;
+       cy_bool bloop = cy_true;
+       uint16_t v = 0, last = 0xffff;
+
+       /*
+       * before determining if the mailboxes are ready for more data,
+       * we first check the mailbox interrupt to see if we need to
+       * receive data.  this prevents a dead-lock condition that can
+       * occur when both sides are trying to receive data.
+       */
+       while (last == last) {
+               /*
+               * disable interrupts to be sure we don't process the mailbox
+               * here and have the interrupt routine try to read this data
+               * as well.
+               */
+               mask = cy_as_hal_disable_interrupts();
+
+               /*
+               * see if there is data to be read.
+               */
+               v = cy_as_hal_read_register(dev_p->tag, CY_AS_MEM_P0_INTR_REG);
+               if ((v & CY_AS_MEM_P0_INTR_REG_MBINT) == 0) {
+                       cy_as_hal_enable_interrupts(mask);
+                       break;
+               }
+
+               /*
+               * queue the mailbox data for later processing.
+               * this allows the firmware to move forward and
+               * service the requst from the P port.
+               */
+               cy_as_ll_queue_mailbox_data(dev_p);
+
+               /*
+               * enable interrupts again to service mailbox
+               * interrupts appropriately
+               */
+               cy_as_hal_enable_interrupts(mask);
+       }
+
+       /*
+       * now, all data is received
+       */
+       last = cy_as_hal_read_register(dev_p->tag,
+               CY_AS_MEM_MCU_MB_STAT) & CY_AS_MEM_P0_MCU_MBNOTRD;
+       while (bloop) {
+               v = cy_as_hal_read_register(dev_p->tag,
+               CY_AS_MEM_MCU_MB_STAT) & CY_AS_MEM_P0_MCU_MBNOTRD;
+               if (v == last)
+                       break;
+
+               last = v;
+       }
+
+       return v;
+}
+
+/*
+* Send a single request or response using the mail box register.
+* This function does not deal with the internal queues at all,
+* but only sends the request or response across to the firmware
+*/
+static cy_as_return_status_t
+cy_as_send_one(
+                       cy_as_device *dev_p,
+                       cy_as_ll_request_response *req_p)
+{
+       int i;
+       uint16_t mb0, v;
+       int32_t loopcount;
+       uint32_t int_stat;
+
+#ifdef _DEBUG
+       if (cy_as_ll_request_response__is_request(req_p)) {
+               switch (cy_as_ll_request_response__get_context(req_p)) {
+               case CY_RQT_GENERAL_RQT_CONTEXT:
+                       cy_as_hal_assert(req_p->length * 2 + 2 <
+                               CY_CTX_GEN_MAX_DATA_SIZE);
+                       break;
+
+               case CY_RQT_RESOURCE_RQT_CONTEXT:
+                       cy_as_hal_assert(req_p->length * 2 + 2 <
+                               CY_CTX_RES_MAX_DATA_SIZE);
+                       break;
+
+               case CY_RQT_STORAGE_RQT_CONTEXT:
+                       cy_as_hal_assert(req_p->length * 2 + 2 <
+                               CY_CTX_STR_MAX_DATA_SIZE);
+                       break;
+
+               case CY_RQT_USB_RQT_CONTEXT:
+                       cy_as_hal_assert(req_p->length * 2 + 2 <
+                               CY_CTX_USB_MAX_DATA_SIZE);
+                       break;
+               }
+       }
+#endif
+
+       /* Write the request to the mail box registers */
+       if (req_p->length > 3) {
+               uint16_t length = req_p->length;
+               int which = 0;
+               int st = 1;
+
+               dev_p->ll_sending_rqt = cy_true;
+               while (which < length) {
+                       loopcount = cy_as_low_level_timeout_count;
+                       do {
+                               v = cy_as_read_intr_status(dev_p);
+
+                       } while (v && loopcount-- > 0);
+
+                       if (v) {
+                               cy_as_hal_print_message(
+                                       ">>>>>> LOW LEVEL TIMEOUT "
+                                       "%x %x %x %x\n",
+                                       cy_as_hal_read_register(dev_p->tag,
+                                               CY_AS_MEM_MCU_MAILBOX0),
+                                       cy_as_hal_read_register(dev_p->tag,
+                                               CY_AS_MEM_MCU_MAILBOX1),
+                                       cy_as_hal_read_register(dev_p->tag,
+                                               CY_AS_MEM_MCU_MAILBOX2),
+                                       cy_as_hal_read_register(dev_p->tag,
+                                               CY_AS_MEM_MCU_MAILBOX3));
+                               return CY_AS_ERROR_TIMEOUT;
+                       }
+
+                       if (dev_p->ll_abort_curr_rqt) {
+                               dev_p->ll_sending_rqt = cy_false;
+                               dev_p->ll_abort_curr_rqt = cy_false;
+                               return CY_AS_ERROR_CANCELED;
+                       }
+
+                       int_stat = cy_as_hal_disable_interrupts();
+
+                       /*
+                        * check again whether the mailbox is free.
+                        * it is possible that an ISR came in and
+                        * wrote into the mailboxes since we last
+                        * checked the status.
+                        */
+                       v = cy_as_hal_read_register(dev_p->tag,
+                               CY_AS_MEM_MCU_MB_STAT) &
+                               CY_AS_MEM_P0_MCU_MBNOTRD;
+                       if (v) {
+                               /* Go back to the original check since
+                                * the mailbox is not free. */
+                               cy_as_hal_enable_interrupts(int_stat);
+                               continue;
+                       }
+
+                       if (which == 0) {
+                               cy_as_hal_write_register(dev_p->tag,
+                                       CY_AS_MEM_MCU_MAILBOX1, length);
+                               st = 2;
+                       } else {
+                               st = 1;
+                       }
+
+                       while ((which < length) && (st < 4)) {
+                               cy_as_hal_write_register(dev_p->tag,
+                                       cy_cast_int2U_int16
+                                               (CY_AS_MEM_MCU_MAILBOX0 + st),
+                                               req_p->data[which++]);
+                               st++;
+                       }
+
+                       mb0 = req_p->box0;
+                       if (which == length) {
+                               dev_p->ll_sending_rqt = cy_false;
+                               mb0 |= CY_AS_REQUEST_RESPONSE_LAST_MASK;
+                       }
+
+                       if (dev_p->ll_abort_curr_rqt) {
+                               dev_p->ll_sending_rqt = cy_false;
+                               dev_p->ll_abort_curr_rqt = cy_false;
+                               cy_as_hal_enable_interrupts(int_stat);
+                               return CY_AS_ERROR_CANCELED;
+                       }
+
+                       cy_as_hal_write_register(dev_p->tag,
+                               CY_AS_MEM_MCU_MAILBOX0, mb0);
+
+                       /* Wait for the MBOX interrupt to be high */
+                       cy_as_hal_sleep150();
+                       cy_as_hal_enable_interrupts(int_stat);
+               }
+       } else {
+check_mailbox_availability:
+               /*
+               * wait for the mailbox registers to become available. this
+               * should be a very quick wait as the firmware is designed
+               * to accept requests at interrupt time and queue them for
+               * future processing.
+               */
+               loopcount = cy_as_low_level_timeout_count;
+               do {
+                       v = cy_as_read_intr_status(dev_p);
+
+               } while (v && loopcount-- > 0);
+
+               if (v) {
+                       cy_as_hal_print_message(
+                               ">>>>>> LOW LEVEL TIMEOUT %x %x %x %x\n",
+                               cy_as_hal_read_register(dev_p->tag,
+                                       CY_AS_MEM_MCU_MAILBOX0),
+                               cy_as_hal_read_register(dev_p->tag,
+                                       CY_AS_MEM_MCU_MAILBOX1),
+                               cy_as_hal_read_register(dev_p->tag,
+                                       CY_AS_MEM_MCU_MAILBOX2),
+                               cy_as_hal_read_register(dev_p->tag,
+                                       CY_AS_MEM_MCU_MAILBOX3));
+                       return CY_AS_ERROR_TIMEOUT;
+               }
+
+               int_stat = cy_as_hal_disable_interrupts();
+
+               /*
+                * check again whether the mailbox is free. it is
+                * possible that an ISR came in and wrote into the
+                * mailboxes since we last checked the status.
+                */
+               v = cy_as_hal_read_register(dev_p->tag, CY_AS_MEM_MCU_MB_STAT) &
+                       CY_AS_MEM_P0_MCU_MBNOTRD;
+               if (v) {
+                       /* Go back to the original check
+                        * since the mailbox is not free. */
+                       cy_as_hal_enable_interrupts(int_stat);
+                       goto check_mailbox_availability;
+               }
+
+               /* Write the data associated with the request
+                * into the mbox registers 1 - 3 */
+               v = 0;
+               for (i = req_p->length - 1; i >= 0; i--)
+                       cy_as_hal_write_register(dev_p->tag,
+                               cy_cast_int2U_int16(CY_AS_MEM_MCU_MAILBOX1 + i),
+                               req_p->data[i]);
+
+               /* Write the mbox register 0 to trigger the interrupt */
+               cy_as_hal_write_register(dev_p->tag, CY_AS_MEM_MCU_MAILBOX0,
+                       req_p->box0 | CY_AS_REQUEST_RESPONSE_LAST_MASK);
+
+               cy_as_hal_sleep150();
+               cy_as_hal_enable_interrupts(int_stat);
+       }
+
+       return CY_AS_ERROR_SUCCESS;
+}
+
+/*
+* This function queues a single request to be sent to the firmware.
+*/
+extern cy_as_return_status_t
+cy_as_ll_send_request(
+                                 cy_as_device *dev_p,
+                                 /* The request to send */
+                                 cy_as_ll_request_response *req,
+                                 /* Storage for a reply, must be sure
+                                  * it is of sufficient size */
+                                 cy_as_ll_request_response *resp,
+                                 /* If true, this is a synchronous request */
+                                 cy_bool sync,
+                                 /* Callback to call when reply is received */
+                                 cy_as_response_callback cb
+)
+{
+       cy_as_context *ctxt_p;
+       uint16_t box0 = req->box0;
+       uint8_t context;
+       cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS;
+       cy_as_ll_request_list_node *node_p;
+       uint32_t mask, state;
+
+       cy_as_hal_assert(dev_p->sig == CY_AS_DEVICE_HANDLE_SIGNATURE);
+
+       context = cy_as_mbox_get_context(box0);
+       cy_as_hal_assert(context < CY_RQT_CONTEXT_COUNT);
+       ctxt_p = dev_p->context[context];
+
+       /* Allocate the list node */
+       state = cy_as_hal_disable_interrupts();
+       node_p = cy_as_hal_c_b_alloc(sizeof(cy_as_ll_request_list_node));
+       cy_as_hal_enable_interrupts(state);
+
+       if (node_p == 0)
+               return CY_AS_ERROR_OUT_OF_MEMORY;
+
+       /* Initialize the list node */
+       node_p->callback = cb;
+       node_p->length = 0;
+       node_p->next = 0;
+       node_p->resp = resp;
+       node_p->rqt = req;
+       node_p->state = CY_AS_REQUEST_LIST_STATE_QUEUED;
+       if (sync)
+               cy_as_request_node_set_sync(node_p);
+
+       /* Put the request into the queue */
+       mask = cy_as_hal_disable_interrupts();
+       if (ctxt_p->request_queue_p == 0) {
+               /* Empty queue */
+               ctxt_p->request_queue_p = node_p;
+               ctxt_p->last_node_p = node_p;
+       } else {
+               ctxt_p->last_node_p->next = node_p;
+               ctxt_p->last_node_p = node_p;
+       }
+       cy_as_hal_enable_interrupts(mask);
+       cy_as_ll_send_next_request(dev_p, ctxt_p);
+
+       if (!cy_as_device_is_in_callback(dev_p)) {
+               mask = cy_as_hal_disable_interrupts();
+               cy_as_mail_box_queued_data_handler(dev_p);
+               cy_as_hal_enable_interrupts(mask);
+       }
+
+       return ret;
+}
+
+static void
+cy_as_ll_send_callback(
+                                  cy_as_device *dev_p,
+                                  uint8_t context,
+                                  cy_as_ll_request_response *rqt,
+                                  cy_as_ll_request_response *resp,
+                                  cy_as_return_status_t ret)
+{
+       (void)rqt;
+       (void)resp;
+       (void)ret;
+
+
+       cy_as_hal_assert(dev_p->sig == CY_AS_DEVICE_HANDLE_SIGNATURE);
+
+       /*
+       * storage the state to return to the caller
+       */
+       dev_p->ll_error = ret;
+
+       /*
+       * now wake the caller
+       */
+       cy_as_hal_wake(&dev_p->context[context]->channel);
+}
+
+cy_as_return_status_t
+cy_as_ll_send_request_wait_reply(
+               cy_as_device *dev_p,
+               /* The request to send */
+               cy_as_ll_request_response *req,
+               /* Storage for a reply, must be
+                * sure it is of sufficient size */
+               cy_as_ll_request_response *resp
+               )
+{
+       cy_as_return_status_t ret;
+       uint8_t context;
+       /* Larger 8 sec time-out to handle the init
+        * delay for slower storage devices in USB FS. */
+       uint32_t loopcount = 800;
+       cy_as_context *ctxt_p;
+
+       /* Get the context for the request */
+       context = cy_as_ll_request_response__get_context(req);
+       cy_as_hal_assert(context < CY_RQT_CONTEXT_COUNT);
+       ctxt_p = dev_p->context[context];
+
+       ret = cy_as_ll_send_request(dev_p, req, resp,
+               cy_true, cy_as_ll_send_callback);
+       if (ret != CY_AS_ERROR_SUCCESS)
+               return ret;
+
+       while (loopcount-- > 0) {
+               /*
+               * sleep while we wait on the response.  receiving the reply will
+               * wake this thread.  we will wait, at most 2 seconds (10 ms*200
+               * tries) before we timeout.  note if the reply arrives, we will
+               * not sleep the entire 10 ms, just til the reply arrives.
+               */
+               cy_as_hal_sleep_on(&ctxt_p->channel, 10);
+
+               /*
+               * if the request has left the queue, it means the request has
+               * been sent and the reply has been received.  this means we can
+               * return to the caller and be sure the reply has been received.
+               */
+               if (!cy_as_ll_is_in_queue(ctxt_p, req))
+                       return dev_p->ll_error;
+       }
+
+       /* Remove the QueueListNode for this request. */
+       cy_as_ll_remove_request(dev_p, ctxt_p, req, cy_true);
+
+       return CY_AS_ERROR_TIMEOUT;
+}
+
+cy_as_return_status_t
+cy_as_ll_register_request_callback(
+                       cy_as_device *dev_p,
+                       uint8_t context,
+                       cy_as_response_callback cb)
+{
+       cy_as_context *ctxt_p;
+       cy_as_hal_assert(context < CY_RQT_CONTEXT_COUNT);
+       ctxt_p = dev_p->context[context];
+
+       ctxt_p->request_callback = cb;
+       return CY_AS_ERROR_SUCCESS;
+}
+
+void
+cy_as_ll_request_response__pack(
+                       cy_as_ll_request_response *req_p,
+                       uint32_t offset,
+                       uint32_t length,
+                       void *data_p)
+{
+       uint16_t dt;
+       uint8_t *dp = (uint8_t *)data_p;
+
+       while (length > 1) {
+               dt = ((*dp++) << 8);
+               dt |= (*dp++);
+               cy_as_ll_request_response__set_word(req_p, offset, dt);
+               offset++;
+               length -= 2;
+       }
+
+       if (length == 1) {
+               dt = (*dp << 8);
+               cy_as_ll_request_response__set_word(req_p, offset, dt);
+       }
+}
+
+void
+cy_as_ll_request_response__unpack(
+                       cy_as_ll_request_response *req_p,
+                       uint32_t offset,
+                       uint32_t length,
+                       void *data_p)
+{
+       uint8_t *dp = (uint8_t *)data_p;
+
+       while (length-- > 0) {
+               uint16_t val = cy_as_ll_request_response__get_word
+                       (req_p, offset++);
+               *dp++ = (uint8_t)((val >> 8) & 0xff);
+
+               if (length) {
+                       length--;
+                       *dp++ = (uint8_t)(val & 0xff);
+               }
+       }
+}
+
+extern cy_as_return_status_t
+cy_as_ll_send_status_response(
+                                                cy_as_device *dev_p,
+                                                uint8_t context,
+                                                uint16_t code,
+                                                uint8_t clear_storage)
+{
+       cy_as_return_status_t ret;
+       cy_as_ll_request_response resp;
+       cy_as_ll_request_response *resp_p = &resp;
+
+       cy_as_hal_mem_set(resp_p, 0, sizeof(resp));
+       resp_p->length = 1;
+       cy_as_ll_request_response__set_response(resp_p);
+       cy_as_ll_request_response__set_context(resp_p, context);
+
+       if (clear_storage)
+               cy_as_ll_request_response__set_clear_storage_flag(resp_p);
+
+       cy_as_ll_request_response__set_code(resp_p, CY_RESP_SUCCESS_FAILURE);
+       cy_as_ll_request_response__set_word(resp_p, 0, code);
+
+       ret = cy_as_send_one(dev_p, resp_p);
+
+       return ret;
+}
+
+extern cy_as_return_status_t
+cy_as_ll_send_data_response(
+                                          cy_as_device *dev_p,
+                                          uint8_t context,
+                                          uint16_t code,
+                                          uint16_t length,
+                                          void *data)
+{
+       cy_as_ll_request_response *resp_p;
+       uint16_t wlen;
+       uint8_t respbuf[256];
+
+       if (length > 192)
+               return CY_AS_ERROR_INVALID_SIZE;
+
+       /* Word length for bytes */
+       wlen = length / 2;
+
+       /* If byte length odd, add one more */
+       if (length % 2)
+               wlen++;
+
+       /* One for the length of field */
+       wlen++;
+
+       resp_p = (cy_as_ll_request_response *)respbuf;
+       cy_as_hal_mem_set(resp_p, 0, sizeof(respbuf));
+       resp_p->length = wlen;
+       cy_as_ll_request_response__set_context(resp_p, context);
+       cy_as_ll_request_response__set_code(resp_p, code);
+
+       cy_as_ll_request_response__set_word(resp_p, 0, length);
+       cy_as_ll_request_response__pack(resp_p, 1, length, data);
+
+       return cy_as_send_one(dev_p, resp_p);
+}
+
+static cy_bool
+cy_as_ll_is_e_p_transfer_related_request(cy_as_ll_request_response *rqt_p,
+       cy_as_end_point_number_t ep)
+{
+       uint16_t v;
+       uint8_t  type = cy_as_ll_request_response__get_code(rqt_p);
+
+       if (cy_as_ll_request_response__get_context(rqt_p) !=
+               CY_RQT_USB_RQT_CONTEXT)
+               return cy_false;
+
+       /*
+        * when cancelling outstanding EP0 data transfers, any pending
+        * setup ACK requests also need to be cancelled.
+        */
+       if ((ep == 0) && (type == CY_RQT_ACK_SETUP_PACKET))
+               return cy_true;
+
+       if (type != CY_RQT_USB_EP_DATA)
+               return cy_false;
+
+       v = cy_as_ll_request_response__get_word(rqt_p, 0);
+       if ((cy_as_end_point_number_t)((v >> 13) & 1) != ep)
+               return cy_false;
+
+       return cy_true;
+}
+
+cy_as_return_status_t
+cy_as_ll_remove_ep_data_requests(cy_as_device *dev_p,
+       cy_as_end_point_number_t ep)
+{
+       cy_as_context *ctxt_p;
+       cy_as_ll_request_list_node *node_p;
+       uint32_t imask;
+
+       /*
+       * first, remove any queued requests
+       */
+       ctxt_p = dev_p->context[CY_RQT_USB_RQT_CONTEXT];
+       if (ctxt_p) {
+               for (node_p = ctxt_p->request_queue_p; node_p;
+                       node_p = node_p->next) {
+                       if (cy_as_ll_is_e_p_transfer_related_request
+                       (node_p->rqt, ep)) {
+                               cy_as_ll_remove_request(dev_p, ctxt_p,
+                                       node_p->rqt, cy_false);
+                               break;
+                       }
+               }
+
+               /*
+               * now, deal with any request that may be in transit
+               */
+               imask = cy_as_hal_disable_interrupts();
+
+               if (ctxt_p->request_queue_p != 0 &&
+                       cy_as_ll_is_e_p_transfer_related_request
+                       (ctxt_p->request_queue_p->rqt, ep) &&
+                       cy_as_request_get_node_state(ctxt_p->request_queue_p) ==
+                       CY_AS_REQUEST_LIST_STATE_WAITING) {
+                       cy_as_hal_print_message("need to remove an in-transit "
+                               "request to antioch\n");
+
+                       /*
+                       * if the request has not been fully sent to west bridge
+                       * yet, abort sending. otherwise, terminate the request
+                       * with a CANCELED status. firmware will already have
+                       * terminated this transfer.
+                       */
+                       if (dev_p->ll_sending_rqt)
+                               dev_p->ll_abort_curr_rqt = cy_true;
+                       else {
+                               uint32_t state;
+
+                               node_p = ctxt_p->request_queue_p;
+                               if (node_p->callback)
+                                       node_p->callback(dev_p, ctxt_p->number,
+                                               node_p->rqt, node_p->resp,
+                                               CY_AS_ERROR_CANCELED);
+
+                               ctxt_p->request_queue_p = node_p->next;
+                               state = cy_as_hal_disable_interrupts();
+                               cy_as_hal_c_b_free(node_p);
+                               cy_as_hal_enable_interrupts(state);
+                       }
+               }
+
+               cy_as_hal_enable_interrupts(imask);
+       }
+
+       return CY_AS_ERROR_SUCCESS;
+}