diff options
Diffstat (limited to 'wilink_6_1/Txn/SdioBusDrv.c')
-rw-r--r-- | wilink_6_1/Txn/SdioBusDrv.c | 592 |
1 files changed, 592 insertions, 0 deletions
diff --git a/wilink_6_1/Txn/SdioBusDrv.c b/wilink_6_1/Txn/SdioBusDrv.c new file mode 100644 index 0000000..87da8f5 --- /dev/null +++ b/wilink_6_1/Txn/SdioBusDrv.c @@ -0,0 +1,592 @@ +/* + * SdioBusDrv.c + * + * Copyright(c) 1998 - 2009 Texas Instruments. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Texas Instruments nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +/** \file SdioBusDrv.c + * \brief The SDIO bus driver upper layer. Platform independent. + * Uses the SdioAdapter API. + * Introduces a generic bus-independent API upwards. + * + * \see BusDrv.h, SdioAdapter.h, SdioAdapter.c + */ + +#define __FILE_ID__ FILE_ID_122 +#include "tidef.h" +#include "report.h" +#include "osApi.h" +#include "TxnDefs.h" +#include "SdioAdapter.h" +#include "BusDrv.h" +#include "bmtrace_api.h" + + +/* remove the chipID check when WL6-PG1.0 becomes obsolete (temporary global variable!!) */ +extern TI_BOOL bChipIs1273Pg10; + + +/************************************************************************ + * Defines + ************************************************************************/ +#define MAX_TXN_PARTS MAX_XFER_BUFS * 2 /* Max number of txn parts derived from one TxnStruct */ + + +/************************************************************************ + * Types + ************************************************************************/ + +/* A single SDIO bus transaction which is a part of a complete transaction (TTxnStruct) */ +typedef struct +{ + TI_BOOL bBlkMode; /* If TRUE this is a block-mode SDIO transaction */ + TI_UINT32 uLength; /* Length in byte */ + TI_UINT32 uHwAddr; /* The device address to write to or read from */ + void * pHostAddr; /* The host buffer address to write from or read into */ + TI_BOOL bMore; /* If TRUE, indicates the lower driver to keep awake for more transactions */ +} TTxnPart; + + +/* The busDrv module Object */ +typedef struct _TBusDrvObj +{ + TI_HANDLE hOs; + TI_HANDLE hReport; + + TBusDrvTxnDoneCb fTxnDoneCb; /* The callback to call upon full transaction completion. */ + TI_HANDLE hCbHandle; /* The callback handle */ + TTxnStruct * pCurrTxn; /* The transaction currently being processed */ + ETxnStatus eCurrTxnStatus; /* COMPLETE, PENDING or ERROR */ + TTxnPart aTxnParts[MAX_TXN_PARTS]; /* The actual bus transactions of current transaction */ + TI_UINT32 uCurrTxnPartsNum; /* Number of transaction parts composing the current transaction */ + TI_UINT32 uCurrTxnPartsCount; /* Number of transaction parts already executed */ + TI_UINT32 uCurrTxnPartsCountSync; /* Number of transaction parts completed in Sync mode (returned COMPLETE) */ + TI_UINT32 uBlkSizeShift; /* In block-mode: uBlkSize = (1 << uBlkSizeShift) = 512 bytes */ + TI_UINT32 uBlkSize; /* In block-mode: uBlkSize = (1 << uBlkSizeShift) = 512 bytes */ + TI_UINT32 uBlkSizeMask; /* In block-mode: uBlkSizeMask = uBlkSize - 1 = 0x1FF*/ + TI_UINT8 * pDmaBuffer; /* DMA-able buffer for buffering all write transactions */ + +} TBusDrvObj; + + +/************************************************************************ + * Internal functions prototypes + ************************************************************************/ +static void busDrv_PrepareTxnParts (TBusDrvObj *pBusDrv, TTxnStruct *pTxn); +static void busDrv_SendTxnParts (TBusDrvObj *pBusDrv); +static void busDrv_TxnDoneCb (TI_HANDLE hBusDrv, TI_INT32 status); + + + +/************************************************************************ + * + * Module functions implementation + * + ************************************************************************/ + +/** + * \fn busDrv_Create + * \brief Create the module + * + * Create and clear the bus driver's object, and the SDIO-adapter. + * + * \note + * \param hOs - Handle to Os Abstraction Layer + * \return Handle of the allocated object, NULL if allocation failed + * \sa busDrv_Destroy + */ +TI_HANDLE busDrv_Create (TI_HANDLE hOs) +{ + TI_HANDLE hBusDrv; + TBusDrvObj *pBusDrv; + + hBusDrv = os_memoryAlloc(hOs, sizeof(TBusDrvObj)); + if (hBusDrv == NULL) + { + return NULL; + } + + pBusDrv = (TBusDrvObj *)hBusDrv; + + os_memoryZero(hOs, hBusDrv, sizeof(TBusDrvObj)); + + pBusDrv->hOs = hOs; + + return pBusDrv; +} + + +/** + * \fn busDrv_Destroy + * \brief Destroy the module. + * + * Close SDIO lower bus driver and free the module's object. + * + * \note + * \param The module's object + * \return TI_OK on success or TI_NOK on failure + * \sa busDrv_Create + */ +TI_STATUS busDrv_Destroy (TI_HANDLE hBusDrv) +{ + TBusDrvObj *pBusDrv = (TBusDrvObj*)hBusDrv; + + if (pBusDrv) + { + os_memoryFree (pBusDrv->hOs, pBusDrv, sizeof(TBusDrvObj)); + } + return TI_OK; +} + + +/** + * \fn busDrv_Init + * \brief Init bus driver + * + * Init module parameters. + + * \note + * \param hBusDrv - The module's handle + * \param hReport - report module handle + * \return void + * \sa + */ +void busDrv_Init (TI_HANDLE hBusDrv, TI_HANDLE hReport) +{ + TBusDrvObj *pBusDrv = (TBusDrvObj*) hBusDrv; + + pBusDrv->hReport = hReport; +} + + +/** + * \fn busDrv_ConnectBus + * \brief Configure bus driver + * + * Called by TxnQ. + * Configure the bus driver with its connection configuration (such as baud-rate, bus width etc) + * and establish the physical connection. + * Done once upon init (and not per functional driver startup). + * + * \note + * \param hBusDrv - The module's object + * \param pBusDrvCfg - A union used for per-bus specific configuration. + * \param fCbFunc - CB function for Async transaction completion (after all txn parts are completed). + * \param hCbArg - The CB function handle + * \return TI_OK / TI_NOK + * \sa + */ +TI_STATUS busDrv_ConnectBus (TI_HANDLE hBusDrv, + TBusDrvCfg *pBusDrvCfg, + TBusDrvTxnDoneCb fCbFunc, + TI_HANDLE hCbArg, + TBusDrvTxnDoneCb fConnectCbFunc) +{ + TBusDrvObj *pBusDrv = (TBusDrvObj*)hBusDrv; + int iStatus; + + /* Save the parameters (TxnQ callback for TxnDone events, and block-size) */ + pBusDrv->fTxnDoneCb = fCbFunc; + pBusDrv->hCbHandle = hCbArg; + pBusDrv->uBlkSizeShift = pBusDrvCfg->tSdioCfg.uBlkSizeShift; + pBusDrv->uBlkSize = 1 << pBusDrv->uBlkSizeShift; + pBusDrv->uBlkSizeMask = pBusDrv->uBlkSize - 1; + /* This should cover stop send Txn parts in recovery */ + pBusDrv->uCurrTxnPartsCount = 0; + pBusDrv->uCurrTxnPartsNum = 0; + pBusDrv->uCurrTxnPartsCountSync = 0; + + + /* + * Configure the SDIO driver parameters and handle SDIO enumeration. + * + * Note: The DMA-able buffer address to use for write transactions is provided from the + * SDIO driver into pBusDrv->pDmaBuffer. + */ + iStatus = sdioAdapt_ConnectBus (busDrv_TxnDoneCb, + hBusDrv, + pBusDrv->uBlkSizeShift, + pBusDrvCfg->tSdioCfg.uBusDrvThreadPriority, + &pBusDrv->pDmaBuffer); + + if (pBusDrv->pDmaBuffer == NULL) + { + TRACE0(pBusDrv->hReport, REPORT_SEVERITY_ERROR, "busDrv_ConnectBus: Didn't get DMA buffer from SDIO driver!!"); + return TI_NOK; + } + + + if (iStatus == 0) + { + return TI_OK; + } + else + { + TRACE2(pBusDrv->hReport, REPORT_SEVERITY_ERROR, "busDrv_ConnectBus: Status = 0x%x, BlkSize = %d\n", iStatus, pBusDrv->uBlkSize); + return TI_NOK; + } +} + + +/** + * \fn busDrv_DisconnectBus + * \brief Disconnect SDIO driver + * + * Called by TxnQ. Disconnect the SDIO driver. + * + * \note + * \param hBusDrv - The module's object + * \return TI_OK / TI_NOK + * \sa + */ +TI_STATUS busDrv_DisconnectBus (TI_HANDLE hBusDrv) +{ + TBusDrvObj *pBusDrv = (TBusDrvObj*)hBusDrv; + + TRACE0(pBusDrv->hReport, REPORT_SEVERITY_INFORMATION, "busDrv_DisconnectBus()\n"); + + /* Disconnect SDIO driver */ + return sdioAdapt_DisconnectBus (); +} + + +/** + * \fn busDrv_Transact + * \brief Process transaction + * + * Called by the TxnQ module to initiate a new transaction. + * Prepare the transaction parts (lower layer single transactions), + * and send them one by one to the lower layer. + * + * \note It's assumed that this function is called only when idle (i.e. previous Txn is done). + * \param hBusDrv - The module's object + * \param pTxn - The transaction object + * \return COMPLETE if Txn completed in this context, PENDING if not, ERROR if failed + * \sa busDrv_PrepareTxnParts, busDrv_SendTxnParts + */ +ETxnStatus busDrv_Transact (TI_HANDLE hBusDrv, TTxnStruct *pTxn) +{ + TBusDrvObj *pBusDrv = (TBusDrvObj*)hBusDrv; + CL_TRACE_START_L4(); + + pBusDrv->pCurrTxn = pTxn; + pBusDrv->uCurrTxnPartsCount = 0; + pBusDrv->uCurrTxnPartsCountSync = 0; + + /* Prepare the transaction parts in a table. */ + busDrv_PrepareTxnParts (pBusDrv, pTxn); + + /* Send the prepared transaction parts. */ + busDrv_SendTxnParts (pBusDrv); + + TRACE1(pBusDrv->hReport, REPORT_SEVERITY_INFORMATION, "busDrv_Transact: Status = %d\n", pBusDrv->eCurrTxnStatus); + + CL_TRACE_END_L4("tiwlan_drv.ko", "INHERIT", "TXN", ".Transact"); + + /* return transaction status - COMPLETE, PENDING or ERROR */ + /* The status is updated in busDrv_SendTxnParts(). It is Async (pending) if not completed in this context */ + return pBusDrv->eCurrTxnStatus; +} + + +/** + * \fn busDrv_PrepareTxnParts + * \brief Prepare write or read transaction parts + * + * Called by busDrv_Transact(). + * Prepares the actual sequence of SDIO bus transactions in a table. + * Use a DMA-able buffer for the bus transaction, so all data is copied + * to it from the host buffer(s) before write transactions, + * or copied from it to the host buffers after read transactions. + * + * \note + * \param pBusDrv - The module's object + * \param pTxn - The transaction object + * \return void + * \sa busDrv_Transact, busDrv_SendTxnParts, + */ +static void busDrv_PrepareTxnParts (TBusDrvObj *pBusDrv, TTxnStruct *pTxn) +{ + TI_UINT32 uPartNum = 0; + TI_UINT32 uTxnLength = 0; + TI_UINT8 *pHostBuf = pBusDrv->pDmaBuffer; /* Host buffer to use for actual transaction is the DMA buffer */ + TI_UINT32 uCurrHwAddr = pTxn->uHwAddr; + TI_BOOL bFixedHwAddr = TXN_PARAM_GET_FIXED_ADDR(pTxn); + TI_UINT32 uBufNum; + TI_UINT32 uBufLen; + TI_UINT32 uRemainderLen; + + /* Go over the transaction buffers */ + for (uBufNum = 0; uBufNum < MAX_XFER_BUFS; uBufNum++) + { + uBufLen = pTxn->aLen[uBufNum]; + + /* If no more buffers, exit the loop */ + if (uBufLen == 0) + { + break; + } + + /* For write transaction, copy the data to the DMA buffer */ + if (TXN_PARAM_GET_DIRECTION(pTxn) == TXN_DIRECTION_WRITE) + { + os_memoryCopy (pBusDrv->hOs, pHostBuf + uTxnLength, pTxn->aBuf[uBufNum], uBufLen); + } + + /* Add buffer length to total transaction length */ + uTxnLength += uBufLen; + } + + /* If current buffer has a remainder, prepare its transaction part */ + uRemainderLen = uTxnLength & pBusDrv->uBlkSizeMask; + if (uRemainderLen > 0) + { + pBusDrv->aTxnParts[uPartNum].bBlkMode = TI_FALSE; + pBusDrv->aTxnParts[uPartNum].uLength = uRemainderLen; + pBusDrv->aTxnParts[uPartNum].uHwAddr = uCurrHwAddr; + pBusDrv->aTxnParts[uPartNum].pHostAddr = (void *)pHostBuf; + pBusDrv->aTxnParts[uPartNum].bMore = TI_TRUE; + + /* If not fixed HW address, increment it by this part's size */ + if (!bFixedHwAddr) + { + uCurrHwAddr += uRemainderLen; + } + + uPartNum++; + } + + /* SDIO block-mode doesn't work on PG1.0 so split to 512 bytes blocks! + Remove when PG1.0 is obsolete! */ + if (bChipIs1273Pg10) + { + TI_UINT32 uLen; + + for (uLen = uRemainderLen; uLen < uTxnLength; uLen += pBusDrv->uBlkSize) + { + pBusDrv->aTxnParts[uPartNum].bBlkMode = TI_FALSE; + pBusDrv->aTxnParts[uPartNum].uLength = pBusDrv->uBlkSize; + pBusDrv->aTxnParts[uPartNum].uHwAddr = uCurrHwAddr; + pBusDrv->aTxnParts[uPartNum].pHostAddr = (void *)(pHostBuf + uLen); + pBusDrv->aTxnParts[uPartNum].bMore = TI_TRUE; + + /* If not fixed HW address, increment it by this part's size */ + if (!bFixedHwAddr) + { + uCurrHwAddr += pBusDrv->uBlkSize; + } + + uPartNum++; + } + } + + + /* For PG2.0, use SDIO block mode */ + else + { + /* If current buffer has full SDIO blocks, prepare a block-mode transaction part */ + if (uTxnLength >= pBusDrv->uBlkSize) + { + pBusDrv->aTxnParts[uPartNum].bBlkMode = TI_TRUE; + pBusDrv->aTxnParts[uPartNum].uLength = uTxnLength - uRemainderLen; + pBusDrv->aTxnParts[uPartNum].uHwAddr = uCurrHwAddr; + pBusDrv->aTxnParts[uPartNum].pHostAddr = (void *)(pHostBuf + uRemainderLen); + pBusDrv->aTxnParts[uPartNum].bMore = TI_TRUE; + + uPartNum++; + } + } + + /* Set last More flag as specified for the whole Txn */ + pBusDrv->aTxnParts[uPartNum - 1].bMore = TXN_PARAM_GET_MORE(pTxn); + pBusDrv->uCurrTxnPartsNum = uPartNum; +} + + +/** + * \fn busDrv_SendTxnParts + * \brief Send prepared transaction parts + * + * Called first by busDrv_Transact(), and also from TxnDone CB after Async completion. + * Sends the prepared transaction parts in a loop. + * If a transaction part is Async, the loop continues later in the TxnDone ISR context. + * When all parts are done, the upper layer TxnDone CB is called. + * + * \note + * \param pBusDrv - The module's object + * \return void + * \sa busDrv_Transact, busDrv_PrepareTxnParts + */ +static void busDrv_SendTxnParts (TBusDrvObj *pBusDrv) +{ + ETxnStatus eStatus; + TTxnPart *pTxnPart; + TTxnStruct *pTxn = pBusDrv->pCurrTxn; + + /* While there are transaction parts to send */ + while (pBusDrv->uCurrTxnPartsCount < pBusDrv->uCurrTxnPartsNum) + { + pTxnPart = &(pBusDrv->aTxnParts[pBusDrv->uCurrTxnPartsCount]); + pBusDrv->uCurrTxnPartsCount++; + + /* Assume pending to be ready in case we are preempted by the TxnDon CB !! */ + pBusDrv->eCurrTxnStatus = TXN_STATUS_PENDING; + + /* If single step, send ELP byte */ + if (TXN_PARAM_GET_SINGLE_STEP(pTxn)) + { + /* Overwrite the function id with function 0 - for ELP register !!!! */ + eStatus = sdioAdapt_TransactBytes (TXN_FUNC_ID_CTRL, + pTxnPart->uHwAddr, + pTxnPart->pHostAddr, + pTxnPart->uLength, + TXN_PARAM_GET_DIRECTION(pTxn), + pTxnPart->bMore); + + /* If first write failed try once again (may happen once upon chip wakeup) */ + if (eStatus == TXN_STATUS_ERROR) + { + /* Overwrite the function id with function 0 - for ELP register !!!! */ + eStatus = sdioAdapt_TransactBytes (TXN_FUNC_ID_CTRL, + pTxnPart->uHwAddr, + pTxnPart->pHostAddr, + pTxnPart->uLength, + TXN_PARAM_GET_DIRECTION(pTxn), + pTxnPart->bMore); + TRACE0(pBusDrv->hReport, REPORT_SEVERITY_WARNING, "busDrv_SendTxnParts: SDIO Single-Step transaction failed once so try again"); + } + } + else + { + eStatus = sdioAdapt_Transact (TXN_PARAM_GET_FUNC_ID(pTxn), + pTxnPart->uHwAddr, + pTxnPart->pHostAddr, + pTxnPart->uLength, + TXN_PARAM_GET_DIRECTION(pTxn), + pTxnPart->bBlkMode, + ((TXN_PARAM_GET_FIXED_ADDR(pTxn) == 1) ? 0 : 1), + pTxnPart->bMore); + } + + TRACE7(pBusDrv->hReport, REPORT_SEVERITY_INFORMATION, "busDrv_SendTxnParts: PartNum = %d, SingleStep = %d, Direction = %d, HwAddr = 0x%x, HostAddr = 0x%x, Length = %d, BlkMode = %d\n", pBusDrv->uCurrTxnPartsCount-1, TXN_PARAM_GET_SINGLE_STEP(pTxn), TXN_PARAM_GET_DIRECTION(pTxn), pTxnPart->uHwAddr, pTxnPart->pHostAddr, pTxnPart->uLength, pTxnPart->bBlkMode); + + /* If pending TxnDone (Async), continue this loop in the next TxnDone interrupt */ + if (eStatus == TXN_STATUS_PENDING) + { + return; + } + + /* Update current transaction status to deduce if it is all finished in the original context (Sync) or not. */ + pBusDrv->eCurrTxnStatus = eStatus; + pBusDrv->uCurrTxnPartsCountSync++; + + /* If error, set error in Txn struct, call TxnDone CB if not fully sync, and exit */ + if (eStatus == TXN_STATUS_ERROR) + { + TXN_PARAM_SET_STATUS(pTxn, TXN_PARAM_STATUS_ERROR); + if (pBusDrv->uCurrTxnPartsCountSync != pBusDrv->uCurrTxnPartsCount) + { + pBusDrv->fTxnDoneCb (pBusDrv->hCbHandle, pTxn); + } + return; + } + } + + /* If we got here we sent all buffers and we don't pend transaction end */ + TRACE3(pBusDrv->hReport, REPORT_SEVERITY_INFORMATION, "busDrv_SendTxnParts: Txn finished successfully, Status = %d, PartsCount = %d, SyncCount = %d\n", pBusDrv->eCurrTxnStatus, pBusDrv->uCurrTxnPartsCount, pBusDrv->uCurrTxnPartsCountSync); + + /* For read transaction, copy the data from the DMA-able buffer to the host buffer(s) */ + if (TXN_PARAM_GET_DIRECTION(pTxn) == TXN_DIRECTION_READ) + { + TI_UINT32 uBufNum; + TI_UINT32 uBufLen; + TI_UINT8 *pDmaBuf = pBusDrv->pDmaBuffer; /* After the read transaction the data is in the DMA buffer */ + + for (uBufNum = 0; uBufNum < MAX_XFER_BUFS; uBufNum++) + { + uBufLen = pTxn->aLen[uBufNum]; + + /* If no more buffers, exit the loop */ + if (uBufLen == 0) + { + break; + } + + os_memoryCopy (pBusDrv->hOs, pTxn->aBuf[uBufNum], pDmaBuf, uBufLen); + pDmaBuf += uBufLen; + } + } + + /* Set status OK in Txn struct, and call TxnDone CB if not fully sync */ + TXN_PARAM_SET_STATUS(pTxn, TXN_PARAM_STATUS_OK); + if (pBusDrv->uCurrTxnPartsCountSync != pBusDrv->uCurrTxnPartsCount) + { + pBusDrv->fTxnDoneCb (pBusDrv->hCbHandle, pTxn); + } +} + + +/** + * \fn busDrv_TxnDoneCb + * \brief Continue async transaction processing (CB) + * + * Called back by the lower (BSP) bus-driver upon Async transaction completion (TxnDone ISR). + * Call busDrv_SendTxnParts to continue sending the remained transaction parts. + * + * \note + * \param hBusDrv - The module's object + * \param status - The last transaction result - 0 = OK, else Error + * \return void + * \sa busDrv_SendTxnParts + */ +static void busDrv_TxnDoneCb (TI_HANDLE hBusDrv, int iStatus) +{ + TBusDrvObj *pBusDrv = (TBusDrvObj*)hBusDrv; + CL_TRACE_START_L1(); + + /* If last transaction part failed, set error in Txn struct, call TxnDone CB and exit. */ + if (iStatus != 0) + { + TRACE1(pBusDrv->hReport, REPORT_SEVERITY_ERROR, "busDrv_TxnDoneCb: Status = 0x%x\n", iStatus); + + TXN_PARAM_SET_STATUS(pBusDrv->pCurrTxn, TXN_PARAM_STATUS_ERROR); + pBusDrv->fTxnDoneCb (pBusDrv->hCbHandle, pBusDrv->pCurrTxn); + CL_TRACE_END_L1("tiwlan_drv.ko", "TXN_DONE", "BusDrvCB", ""); + return; + } + + TRACE0(pBusDrv->hReport, REPORT_SEVERITY_INFORMATION, "busDrv_TxnDoneCb()\n"); + + /* Continue sending the remained transaction parts. */ + busDrv_SendTxnParts (pBusDrv); + + CL_TRACE_END_L1("tiwlan_drv.ko", "TXN_DONE", "BusDrvCB", ""); +} |