Index: chromeos/compat-wireless/drivers/staging/ath6kl/hif/sdio/linux_sdio/src/hif_scatter.c |
diff --git a/chromeos/compat-wireless/drivers/staging/ath6kl/hif/sdio/linux_sdio/src/hif_scatter.c b/chromeos/compat-wireless/drivers/staging/ath6kl/hif/sdio/linux_sdio/src/hif_scatter.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..22c6c6659f5b5f9a004ed25ae4543e01c200380b |
--- /dev/null |
+++ b/chromeos/compat-wireless/drivers/staging/ath6kl/hif/sdio/linux_sdio/src/hif_scatter.c |
@@ -0,0 +1,393 @@ |
+//------------------------------------------------------------------------------ |
+// Copyright (c) 2009-2010 Atheros Corporation. All rights reserved. |
+// |
+// |
+// Permission to use, copy, modify, and/or distribute this software for any |
+// purpose with or without fee is hereby granted, provided that the above |
+// copyright notice and this permission notice appear in all copies. |
+// |
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
+// |
+// |
+//------------------------------------------------------------------------------ |
+//============================================================================== |
+// HIF scatter implementation |
+// |
+// Author(s): ="Atheros" |
+//============================================================================== |
+ |
+#include <linux/mmc/card.h> |
+#include <linux/mmc/host.h> |
+#include <linux/mmc/sdio_func.h> |
+#include <linux/mmc/sdio_ids.h> |
+#include <linux/mmc/sdio.h> |
+#include <linux/kthread.h> |
+#include "hif_internal.h" |
+#define ATH_MODULE_NAME hif |
+#include "a_debug.h" |
+ |
+#ifdef HIF_LINUX_MMC_SCATTER_SUPPORT |
+ |
+#define _CMD53_ARG_READ 0 |
+#define _CMD53_ARG_WRITE 1 |
+#define _CMD53_ARG_BLOCK_BASIS 1 |
+#define _CMD53_ARG_FIXED_ADDRESS 0 |
+#define _CMD53_ARG_INCR_ADDRESS 1 |
+ |
+#define SDIO_SET_CMD53_ARG(arg,rw,func,mode,opcode,address,bytes_blocks) \ |
+ (arg) = (((rw) & 1) << 31) | \ |
+ (((func) & 0x7) << 28) | \ |
+ (((mode) & 1) << 27) | \ |
+ (((opcode) & 1) << 26) | \ |
+ (((address) & 0x1FFFF) << 9) | \ |
+ ((bytes_blocks) & 0x1FF) |
+ |
+static void FreeScatterReq(HIF_DEVICE *device, HIF_SCATTER_REQ *pReq) |
+{ |
+ unsigned long flag; |
+ |
+ spin_lock_irqsave(&device->lock, flag); |
+ |
+ DL_ListInsertTail(&device->ScatterReqHead, &pReq->ListLink); |
+ |
+ spin_unlock_irqrestore(&device->lock, flag); |
+ |
+} |
+ |
+static HIF_SCATTER_REQ *AllocScatterReq(HIF_DEVICE *device) |
+{ |
+ DL_LIST *pItem; |
+ unsigned long flag; |
+ |
+ spin_lock_irqsave(&device->lock, flag); |
+ |
+ pItem = DL_ListRemoveItemFromHead(&device->ScatterReqHead); |
+ |
+ spin_unlock_irqrestore(&device->lock, flag); |
+ |
+ if (pItem != NULL) { |
+ return A_CONTAINING_STRUCT(pItem, HIF_SCATTER_REQ, ListLink); |
+ } |
+ |
+ return NULL; |
+} |
+ |
+ /* called by async task to perform the operation synchronously using direct MMC APIs */ |
+A_STATUS DoHifReadWriteScatter(HIF_DEVICE *device, BUS_REQUEST *busrequest) |
+{ |
+ int i; |
+ A_UINT8 rw; |
+ A_UINT8 opcode; |
+ struct mmc_request mmcreq; |
+ struct mmc_command cmd; |
+ struct mmc_data data; |
+ HIF_SCATTER_REQ_PRIV *pReqPriv; |
+ HIF_SCATTER_REQ *pReq; |
+ A_STATUS status = A_OK; |
+ struct scatterlist *pSg; |
+ |
+ pReqPriv = busrequest->pScatterReq; |
+ |
+ A_ASSERT(pReqPriv != NULL); |
+ |
+ pReq = pReqPriv->pHifScatterReq; |
+ |
+ memset(&mmcreq, 0, sizeof(struct mmc_request)); |
+ memset(&cmd, 0, sizeof(struct mmc_command)); |
+ memset(&data, 0, sizeof(struct mmc_data)); |
+ |
+ data.blksz = HIF_MBOX_BLOCK_SIZE; |
+ data.blocks = pReq->TotalLength / HIF_MBOX_BLOCK_SIZE; |
+ |
+ AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, ("HIF-SCATTER: (%s) Address: 0x%X, (BlockLen: %d, BlockCount: %d) , (tot:%d,sg:%d)\n", |
+ (pReq->Request & HIF_WRITE) ? "WRITE":"READ", pReq->Address, data.blksz, data.blocks, |
+ pReq->TotalLength,pReq->ValidScatterEntries)); |
+ |
+ if (pReq->Request & HIF_WRITE) { |
+ rw = _CMD53_ARG_WRITE; |
+ data.flags = MMC_DATA_WRITE; |
+ } else { |
+ rw = _CMD53_ARG_READ; |
+ data.flags = MMC_DATA_READ; |
+ } |
+ |
+ if (pReq->Request & HIF_FIXED_ADDRESS) { |
+ opcode = _CMD53_ARG_FIXED_ADDRESS; |
+ } else { |
+ opcode = _CMD53_ARG_INCR_ADDRESS; |
+ } |
+ |
+ /* fill SG entries */ |
+ pSg = pReqPriv->sgentries; |
+ sg_init_table(pSg, pReq->ValidScatterEntries); |
+ |
+ /* assemble SG list */ |
+ for (i = 0 ; i < pReq->ValidScatterEntries ; i++, pSg++) { |
+ /* setup each sg entry */ |
+ if ((unsigned long)pReq->ScatterList[i].pBuffer & 0x3) { |
+ /* note some scatter engines can handle unaligned buffers, print this |
+ * as informational only */ |
+ AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, |
+ ("HIF: (%s) Scatter Buffer is unaligned 0x%lx\n", |
+ pReq->Request & HIF_WRITE ? "WRITE":"READ", |
+ (unsigned long)pReq->ScatterList[i].pBuffer)); |
+ } |
+ |
+ AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, (" %d: Addr:0x%lX, Len:%d \n", |
+ i,(unsigned long)pReq->ScatterList[i].pBuffer,pReq->ScatterList[i].Length)); |
+ |
+ sg_set_buf(pSg, pReq->ScatterList[i].pBuffer, pReq->ScatterList[i].Length); |
+ } |
+ /* set scatter-gather table for request */ |
+ data.sg = pReqPriv->sgentries; |
+ data.sg_len = pReq->ValidScatterEntries; |
+ /* set command argument */ |
+ SDIO_SET_CMD53_ARG(cmd.arg, |
+ rw, |
+ device->func->num, |
+ _CMD53_ARG_BLOCK_BASIS, |
+ opcode, |
+ pReq->Address, |
+ data.blocks); |
+ |
+ cmd.opcode = SD_IO_RW_EXTENDED; |
+ cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; |
+ |
+ mmcreq.cmd = &cmd; |
+ mmcreq.data = &data; |
+ |
+ mmc_set_data_timeout(&data, device->func->card); |
+ /* synchronous call to process request */ |
+ mmc_wait_for_req(device->func->card->host, &mmcreq); |
+ |
+ if (cmd.error) { |
+ status = A_ERROR; |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("HIF-SCATTER: cmd error: %d \n",cmd.error)); |
+ } |
+ |
+ if (data.error) { |
+ status = A_ERROR; |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("HIF-SCATTER: data error: %d \n",data.error)); |
+ } |
+ |
+ if (A_FAILED(status)) { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("HIF-SCATTER: FAILED!!! (%s) Address: 0x%X, Block mode (BlockLen: %d, BlockCount: %d)\n", |
+ (pReq->Request & HIF_WRITE) ? "WRITE":"READ",pReq->Address, data.blksz, data.blocks)); |
+ } |
+ |
+ /* set completion status, fail or success */ |
+ pReq->CompletionStatus = status; |
+ |
+ if (pReq->Request & HIF_ASYNCHRONOUS) { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, ("HIF-SCATTER: async_task completion routine req: 0x%lX (%d)\n",(unsigned long)busrequest, status)); |
+ /* complete the request */ |
+ A_ASSERT(pReq->CompletionRoutine != NULL); |
+ pReq->CompletionRoutine(pReq); |
+ } else { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, ("HIF-SCATTER async_task upping busrequest : 0x%lX (%d)\n", (unsigned long)busrequest,status)); |
+ /* signal wait */ |
+ up(&busrequest->sem_req); |
+ } |
+ |
+ return status; |
+} |
+ |
+ /* callback to issue a read-write scatter request */ |
+static A_STATUS HifReadWriteScatter(HIF_DEVICE *device, HIF_SCATTER_REQ *pReq) |
+{ |
+ A_STATUS status = A_EINVAL; |
+ A_UINT32 request = pReq->Request; |
+ HIF_SCATTER_REQ_PRIV *pReqPriv = (HIF_SCATTER_REQ_PRIV *)pReq->HIFPrivate[0]; |
+ |
+ do { |
+ |
+ A_ASSERT(pReqPriv != NULL); |
+ |
+ AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, ("HIF-SCATTER: total len: %d Scatter Entries: %d\n", |
+ pReq->TotalLength, pReq->ValidScatterEntries)); |
+ |
+ if (!(request & HIF_EXTENDED_IO)) { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, |
+ ("HIF-SCATTER: Invalid command type: 0x%08x\n", request)); |
+ break; |
+ } |
+ |
+ if (!(request & (HIF_SYNCHRONOUS | HIF_ASYNCHRONOUS))) { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, |
+ ("HIF-SCATTER: Invalid execution mode: 0x%08x\n", request)); |
+ break; |
+ } |
+ |
+ if (!(request & HIF_BLOCK_BASIS)) { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, |
+ ("HIF-SCATTER: Invalid data mode: 0x%08x\n", request)); |
+ break; |
+ } |
+ |
+ if (pReq->TotalLength > MAX_SCATTER_REQ_TRANSFER_SIZE) { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, |
+ ("HIF-SCATTER: Invalid length: %d \n", pReq->TotalLength)); |
+ break; |
+ } |
+ |
+ if (pReq->TotalLength == 0) { |
+ A_ASSERT(FALSE); |
+ break; |
+ } |
+ |
+ /* add bus request to the async list for the async I/O thread to process */ |
+ AddToAsyncList(device, pReqPriv->busrequest); |
+ |
+ if (request & HIF_SYNCHRONOUS) { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, ("HIF-SCATTER: queued sync req: 0x%lX\n", (unsigned long)pReqPriv->busrequest)); |
+ /* signal thread and wait */ |
+ up(&device->sem_async); |
+ if (down_interruptible(&pReqPriv->busrequest->sem_req) != 0) { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,("HIF-SCATTER: interrupted! \n")); |
+ /* interrupted, exit */ |
+ status = A_ERROR; |
+ break; |
+ } else { |
+ status = pReq->CompletionStatus; |
+ } |
+ } else { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, ("HIF-SCATTER: queued async req: 0x%lX\n", (unsigned long)pReqPriv->busrequest)); |
+ /* wake thread, it will process and then take care of the async callback */ |
+ up(&device->sem_async); |
+ status = A_OK; |
+ } |
+ |
+ } while (FALSE); |
+ |
+ if (A_FAILED(status) && (request & HIF_ASYNCHRONOUS)) { |
+ pReq->CompletionStatus = status; |
+ pReq->CompletionRoutine(pReq); |
+ status = A_OK; |
+ } |
+ |
+ return status; |
+} |
+ |
+ /* setup of HIF scatter resources */ |
+A_STATUS SetupHIFScatterSupport(HIF_DEVICE *device, HIF_DEVICE_SCATTER_SUPPORT_INFO *pInfo) |
+{ |
+ A_STATUS status = A_ERROR; |
+ int i; |
+ HIF_SCATTER_REQ_PRIV *pReqPriv; |
+ BUS_REQUEST *busrequest; |
+ |
+ do { |
+ |
+ /* check if host supports scatter requests and it meets our requirements */ |
+ if (device->func->card->host->max_hw_segs < MAX_SCATTER_ENTRIES_PER_REQ) { |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HIF-SCATTER : host only supports scatter of : %d entries, need: %d \n", |
+ device->func->card->host->max_hw_segs, MAX_SCATTER_ENTRIES_PER_REQ)); |
+ status = A_ENOTSUP; |
+ break; |
+ } |
+ |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("HIF-SCATTER Enabled: max scatter req : %d entries: %d \n", |
+ MAX_SCATTER_REQUESTS, MAX_SCATTER_ENTRIES_PER_REQ)); |
+ |
+ for (i = 0; i < MAX_SCATTER_REQUESTS; i++) { |
+ /* allocate the private request blob */ |
+ pReqPriv = (HIF_SCATTER_REQ_PRIV *)A_MALLOC(sizeof(HIF_SCATTER_REQ_PRIV)); |
+ if (NULL == pReqPriv) { |
+ break; |
+ } |
+ A_MEMZERO(pReqPriv, sizeof(HIF_SCATTER_REQ_PRIV)); |
+ /* save the device instance*/ |
+ pReqPriv->device = device; |
+ /* allocate the scatter request */ |
+ pReqPriv->pHifScatterReq = (HIF_SCATTER_REQ *)A_MALLOC(sizeof(HIF_SCATTER_REQ) + |
+ (MAX_SCATTER_ENTRIES_PER_REQ - 1) * (sizeof(HIF_SCATTER_ITEM))); |
+ |
+ if (NULL == pReqPriv->pHifScatterReq) { |
+ A_FREE(pReqPriv); |
+ break; |
+ } |
+ /* just zero the main part of the scatter request */ |
+ A_MEMZERO(pReqPriv->pHifScatterReq, sizeof(HIF_SCATTER_REQ)); |
+ /* back pointer to the private struct */ |
+ pReqPriv->pHifScatterReq->HIFPrivate[0] = pReqPriv; |
+ /* allocate a bus request for this scatter request */ |
+ busrequest = hifAllocateBusRequest(device); |
+ if (NULL == busrequest) { |
+ A_FREE(pReqPriv->pHifScatterReq); |
+ A_FREE(pReqPriv); |
+ break; |
+ } |
+ /* assign the scatter request to this bus request */ |
+ busrequest->pScatterReq = pReqPriv; |
+ /* point back to the request */ |
+ pReqPriv->busrequest = busrequest; |
+ /* add it to the scatter pool */ |
+ FreeScatterReq(device,pReqPriv->pHifScatterReq); |
+ } |
+ |
+ if (i != MAX_SCATTER_REQUESTS) { |
+ status = A_NO_MEMORY; |
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HIF-SCATTER : failed to alloc scatter resources !\n")); |
+ break; |
+ } |
+ |
+ /* set scatter function pointers */ |
+ pInfo->pAllocateReqFunc = AllocScatterReq; |
+ pInfo->pFreeReqFunc = FreeScatterReq; |
+ pInfo->pReadWriteScatterFunc = HifReadWriteScatter; |
+ pInfo->MaxScatterEntries = MAX_SCATTER_ENTRIES_PER_REQ; |
+ pInfo->MaxTransferSizePerScatterReq = MAX_SCATTER_REQ_TRANSFER_SIZE; |
+ |
+ status = A_OK; |
+ |
+ } while (FALSE); |
+ |
+ if (A_FAILED(status)) { |
+ CleanupHIFScatterResources(device); |
+ } |
+ |
+ return status; |
+} |
+ |
+ /* clean up scatter support */ |
+void CleanupHIFScatterResources(HIF_DEVICE *device) |
+{ |
+ HIF_SCATTER_REQ_PRIV *pReqPriv; |
+ HIF_SCATTER_REQ *pReq; |
+ |
+ /* empty the free list */ |
+ |
+ while (1) { |
+ |
+ pReq = AllocScatterReq(device); |
+ |
+ if (NULL == pReq) { |
+ break; |
+ } |
+ |
+ pReqPriv = (HIF_SCATTER_REQ_PRIV *)pReq->HIFPrivate[0]; |
+ A_ASSERT(pReqPriv != NULL); |
+ |
+ if (pReqPriv->busrequest != NULL) { |
+ pReqPriv->busrequest->pScatterReq = NULL; |
+ /* free bus request */ |
+ hifFreeBusRequest(device, pReqPriv->busrequest); |
+ pReqPriv->busrequest = NULL; |
+ } |
+ |
+ if (pReqPriv->pHifScatterReq != NULL) { |
+ A_FREE(pReqPriv->pHifScatterReq); |
+ pReqPriv->pHifScatterReq = NULL; |
+ } |
+ |
+ A_FREE(pReqPriv); |
+ } |
+} |
+ |
+#endif // HIF_LINUX_MMC_SCATTER_SUPPORT |