summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ath6kl/hif/sdio/linux_sdio/src/hif_scatter.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath6kl/hif/sdio/linux_sdio/src/hif_scatter.c')
-rw-r--r--drivers/net/wireless/ath6kl/hif/sdio/linux_sdio/src/hif_scatter.c390
1 files changed, 390 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath6kl/hif/sdio/linux_sdio/src/hif_scatter.c b/drivers/net/wireless/ath6kl/hif/sdio/linux_sdio/src/hif_scatter.c
new file mode 100644
index 000000000000..de236d269f0d
--- /dev/null
+++ b/drivers/net/wireless/ath6kl/hif/sdio/linux_sdio/src/hif_scatter.c
@@ -0,0 +1,390 @@
+//------------------------------------------------------------------------------
+// <copyright file="hif_scatter.c" company="Atheros">
+// Copyright (c) 2009 Atheros Corporation. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation;
+//
+// Software distributed under the License is distributed on an "AS
+// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+// implied. See the License for the specific language governing
+// rights and limitations under the License.
+//
+//
+//------------------------------------------------------------------------------
+//==============================================================================
+// 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 ((A_UINT32)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%08x\n",
+ pReq->Request & HIF_WRITE ? "WRITE":"READ",
+ (A_UINT32)pReq->ScatterList[i].pBuffer));
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, (" %d: Addr:0x%X, Len:%d \n",
+ i,(A_UINT32)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%X (%d)\n",(unsigned int)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%X (%d)\n", (unsigned int)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%X\n", (unsigned int)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%X\n", (unsigned int)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