Home | History | Annotate | Download | only in linux
      1 /*
      2  * SdioAdapter.c
      3  *
      4  * Copyright(c) 1998 - 2009 Texas Instruments. All rights reserved.
      5  * Copyright(c) 2008 - 2009 Google, Inc. All rights reserved.
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  *
     12  *  * Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  *  * Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in
     16  *    the documentation and/or other materials provided with the
     17  *    distribution.
     18  *  * Neither the name Texas Instruments nor the names of its
     19  *    contributors may be used to endorse or promote products derived
     20  *    from this software without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     25  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     26  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     28  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     32  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     33  */
     34 
     35 /** \file   SdioAdapter.c
     36  *  \brief  The SDIO driver adapter. Platform dependent.
     37  *
     38  * An adaptation layer between the lower SDIO driver (in BSP) and the upper Sdio
     39  * Used for issuing all SDIO transaction types towards the lower SDIO-driver.
     40  * Makes the decision whether to use Sync or Async transaction, and reflects it
     41  *     by the return value and calling its callback in case of Async.
     42  *
     43  *  \see    SdioAdapter.h, SdioDrv.c & h
     44  */
     45 
     46 #ifdef CONFIG_MMC_EMBEDDED_SDIO
     47 #include <linux/kernel.h>
     48 #include <linux/mutex.h>
     49 #include <linux/mmc/core.h>
     50 #include <linux/mmc/card.h>
     51 #include <linux/mmc/sdio_func.h>
     52 #include <linux/mmc/sdio_ids.h>
     53 #include "TxnDefs.h"
     54 
     55 #define TI_SDIO_DEBUG
     56 
     57 #define TIWLAN_MMC_MAX_DMA                 8192
     58 
     59 int wifi_set_carddetect( int on );
     60 
     61 static struct sdio_func *tiwlan_func = NULL;
     62 static struct completion sdio_wait;
     63 
     64 ETxnStatus sdioAdapt_TransactBytes (unsigned int  uFuncId,
     65                                     unsigned int  uHwAddr,
     66                                     void *        pHostAddr,
     67                                     unsigned int  uLength,
     68                                     unsigned int  bDirection,
     69                                     unsigned int  bMore);
     70 
     71 static int sdio_wifi_probe(struct sdio_func *func,
     72                            const struct sdio_device_id *id)
     73 {
     74         int rc;
     75 
     76         printk("%s: %d\n", __FUNCTION__, func->class);
     77 
     78         if (func->class != SDIO_CLASS_WLAN)
     79                 return -EINVAL;
     80 
     81         sdio_claim_host(func);
     82 
     83         rc = sdio_enable_func(func);
     84         if (rc)
     85                 goto err1;
     86         rc = sdio_set_block_size(func, 512);
     87 
     88         if (rc) {
     89                 printk("%s: Unable to set blocksize\n", __FUNCTION__);
     90                 goto err2;
     91         }
     92 
     93         tiwlan_func = func;
     94         complete(&sdio_wait);
     95         return 0;
     96 err2:
     97         sdio_disable_func(func);
     98 err1:
     99         sdio_release_host(func);
    100         complete(&sdio_wait);
    101         return rc;
    102 }
    103 
    104 static void sdio_wifi_remove(struct sdio_func *func)
    105 {
    106 }
    107 
    108 static const struct sdio_device_id sdio_wifi_ids[] = {
    109         { SDIO_DEVICE_CLASS(SDIO_CLASS_WLAN)    },
    110         {                                       },
    111 };
    112 
    113 MODULE_DEVICE_TABLE(sdio, sdio_wifi_ids);
    114 
    115 static struct sdio_driver sdio_wifi_driver = {
    116         .probe          = sdio_wifi_probe,
    117         .remove         = sdio_wifi_remove,
    118         .name           = "sdio_wifi",
    119         .id_table       = sdio_wifi_ids,
    120 };
    121 
    122 ETxnStatus sdioAdapt_TransactBytes (unsigned int  uFuncId,
    123                                     unsigned int  uHwAddr,
    124                                     void *        pHostAddr,
    125                                     unsigned int  uLength,
    126                                     unsigned int  bDirection,
    127                                     unsigned int  bMore);
    128 
    129 int sdioAdapt_ConnectBus (void *        fCbFunc,
    130                           void *        hCbArg,
    131                           unsigned int  uBlkSizeShift,
    132                           unsigned int  uSdioThreadPriority,
    133                           unsigned char **pTxDmaSrcAddr)
    134 {
    135 	int rc;
    136 
    137 	init_completion(&sdio_wait);
    138 	wifi_set_carddetect( 1 );
    139 	rc = sdio_register_driver(&sdio_wifi_driver);
    140 	if (rc < 0) {
    141 		printk(KERN_ERR "%s: Fail to register sdio_wifi_driver\n", __func__);
    142 		return rc;
    143 	}
    144 	if (!wait_for_completion_timeout(&sdio_wait, msecs_to_jiffies(10000))) {
    145 		printk(KERN_ERR "%s: Timed out waiting for device detect\n", __func__);
    146 		sdio_unregister_driver(&sdio_wifi_driver);
    147 		return -ENODEV;
    148 	}
    149 	/* Provide the DMA buffer address to the upper layer so it will use it as the transactions host buffer. */
    150 	if (pTxDmaSrcAddr) { /* Dm: check what to do with it */
    151 		*pTxDmaSrcAddr = kmalloc(TIWLAN_MMC_MAX_DMA, GFP_ATOMIC | GFP_DMA);
    152 	}
    153 	return 0;
    154 }
    155 
    156 int sdioAdapt_DisconnectBus (void)
    157 {
    158 	if (tiwlan_func) {
    159 		sdio_disable_func( tiwlan_func );
    160 		sdio_release_host( tiwlan_func );
    161 	}
    162 	wifi_set_carddetect( 0 );
    163 	sdio_unregister_driver(&sdio_wifi_driver);
    164 	return 0;
    165 }
    166 
    167 ETxnStatus sdioAdapt_TransactBytes (unsigned int  uFuncId,
    168                                     unsigned int  uHwAddr,
    169                                     void *        pHostAddr,
    170                                     unsigned int  uLength,
    171                                     unsigned int  bDirection,
    172                                     unsigned int  bMore)
    173 {
    174 	unsigned char *pData = pHostAddr;
    175 	unsigned int i;
    176 	int rc = 0, final_rc = 0;
    177 
    178 	for (i = 0; i < uLength; i++) {
    179 		if( bDirection ) {
    180 			if (uFuncId == 0)
    181 				*pData = (unsigned char)sdio_f0_readb(tiwlan_func, uHwAddr, &rc);
    182 			else
    183 				*pData = (unsigned char)sdio_readb(tiwlan_func, uHwAddr, &rc);
    184 		}
    185 		else {
    186 			if (uFuncId == 0)
    187 				sdio_f0_writeb(tiwlan_func, *pData, uHwAddr, &rc);
    188 			else
    189 				sdio_writeb(tiwlan_func, *pData, uHwAddr, &rc);
    190 		}
    191 		if( rc ) {
    192 			final_rc = rc;
    193 		}
    194 #ifdef TI_SDIO_DEBUG
    195 		printk(KERN_INFO "%c52: [0x%x](%u) %c 0x%x\n", (bDirection ? 'R' : 'W'), uHwAddr, uLength, (bDirection ? '=' : '<'), (unsigned)*pData);
    196 #endif
    197 		uHwAddr++;
    198 		pData++;
    199 	}
    200 	/* If failed return ERROR, if succeeded return COMPLETE */
    201 	if (final_rc) {
    202 		return TXN_STATUS_ERROR;
    203 	}
    204 	return TXN_STATUS_COMPLETE;
    205 }
    206 
    207 ETxnStatus sdioAdapt_Transact (unsigned int  uFuncId,
    208                                unsigned int  uHwAddr,
    209                                void *        pHostAddr,
    210                                unsigned int  uLength,
    211                                unsigned int  bDirection,
    212                                unsigned int  bBlkMode,
    213                                unsigned int  bFixedAddr,
    214                                unsigned int  bMore)
    215 {
    216 	int rc;
    217 
    218 	if (uFuncId == 0)
    219 		return sdioAdapt_TransactBytes (uFuncId, uHwAddr, pHostAddr,
    220 						uLength, bDirection, bMore);
    221 	if (bDirection) {
    222 		if (bFixedAddr)
    223 			rc = sdio_memcpy_fromio(tiwlan_func, pHostAddr, uHwAddr, uLength);
    224 		else
    225 			rc = sdio_readsb(tiwlan_func, pHostAddr, uHwAddr, uLength);
    226 
    227 	}
    228 	else {
    229 		if (bFixedAddr)
    230 			rc = sdio_memcpy_toio(tiwlan_func, uHwAddr, pHostAddr, uLength);
    231 		else
    232 			rc = sdio_writesb(tiwlan_func, uHwAddr, pHostAddr, uLength);
    233 	}
    234 #ifdef TI_SDIO_DEBUG
    235 	if (uLength == 1)
    236 	        printk(KERN_INFO "%c53: [0x%x](%u) %c 0x%x\n", (bDirection ? 'R' : 'W'), uHwAddr, uLength, (bDirection ? '=' : '<'), (unsigned)(*(char *)pHostAddr));
    237 	else if (uLength == 2)
    238 	        printk(KERN_INFO "%c53: [0x%x](%u) %c 0x%x\n", (bDirection ? 'R' : 'W'), uHwAddr, uLength, (bDirection ? '=' : '<'), (unsigned)(*(short *)pHostAddr));
    239 	else if (uLength == 4)
    240 	        printk(KERN_INFO "%c53: [0x%x](%u) %c 0x%x\n", (bDirection ? 'R' : 'W'), uHwAddr, uLength, (bDirection ? '=' : '<'), (unsigned)(*(long *)pHostAddr));
    241 	else
    242 		printk(KERN_INFO "%c53: [0x%x](%u) F[%d] B[%d] I[%d] = %d\n", (bDirection ? 'R' : 'W'), uHwAddr, uLength, uFuncId, bBlkMode, bFixedAddr, rc);
    243 #endif
    244 	/* If failed return ERROR, if succeeded return COMPLETE */
    245 	if (rc) {
    246 		return TXN_STATUS_ERROR;
    247 	}
    248 	return TXN_STATUS_COMPLETE;
    249 }
    250 
    251 #else
    252 
    253 #include "SdioDrvDbg.h"
    254 #include "TxnDefs.h"
    255 #include "SdioAdapter.h"
    256 #include "SdioDrv.h"
    257 #include "bmtrace_api.h"
    258 
    259 #ifdef SDIO_1_BIT /* see also in SdioDrv.c */
    260 #define SDIO_BITS_CODE   0x80 /* 1 bits */
    261 #else
    262 #define SDIO_BITS_CODE   0x82 /* 4 bits */
    263 #endif
    264 
    265 /************************************************************************
    266  * Defines
    267  ************************************************************************/
    268 /* Sync/Async Threshold */
    269 #ifdef FULL_ASYNC_MODE
    270 #define SYNC_ASYNC_LENGTH_THRESH	0     /* Use Async for all transactions */
    271 #else
    272 #define SYNC_ASYNC_LENGTH_THRESH	360   /* Use Async for transactions longer than this threshold (in bytes) */
    273 #endif
    274 
    275 #define MAX_RETRIES                 10
    276 
    277 /* For block mode configuration */
    278 #define FN0_FBR2_REG_108                    0x210
    279 #define FN0_FBR2_REG_108_BIT_MASK           0xFFF
    280 
    281 int sdioDrv_clk_enable(void);
    282 void sdioDrv_clk_disable(void);
    283 
    284 int sdioAdapt_ConnectBus (void *        fCbFunc,
    285                           void *        hCbArg,
    286                           unsigned int  uBlkSizeShift,
    287                           unsigned int  uSdioThreadPriority,
    288                           unsigned char **pTxDmaSrcAddr)
    289 {
    290 	unsigned char  uByte;
    291 	unsigned long  uLong;
    292 	unsigned long  uCount = 0;
    293 	unsigned int   uBlkSize = 1 << uBlkSizeShift;
    294 	int            iStatus;
    295 
    296 	if (uBlkSize < SYNC_ASYNC_LENGTH_THRESH)
    297 	{
    298 		PERR1("%s(): Block-Size should be bigger than SYNC_ASYNC_LENGTH_THRESH!!\n", __FUNCTION__ );
    299 	}
    300 
    301 	/* Init SDIO driver and HW */
    302 	iStatus = sdioDrv_ConnectBus (fCbFunc, hCbArg, uBlkSizeShift,uSdioThreadPriority, pTxDmaSrcAddr);
    303 	if (iStatus) { return iStatus; }
    304 
    305 	/* Send commands sequence: 0, 5, 3, 7 */
    306 	iStatus = sdioDrv_ExecuteCmd (SD_IO_GO_IDLE_STATE, 0, MMC_RSP_NONE, &uByte, sizeof(uByte));
    307 	if (iStatus)
    308         {
    309            printk("%s %d command number: %d failed\n", __FUNCTION__, __LINE__, SD_IO_GO_IDLE_STATE);
    310            return iStatus;
    311         }
    312 	iStatus = sdioDrv_ExecuteCmd (SDIO_CMD5, VDD_VOLTAGE_WINDOW, MMC_RSP_R4, &uByte, sizeof(uByte));
    313 	if (iStatus) {
    314           printk("%s %d command number: %d failed\n", __FUNCTION__, __LINE__, SDIO_CMD5);
    315           return iStatus;
    316         }
    317 
    318 	iStatus = sdioDrv_ExecuteCmd (SD_IO_SEND_RELATIVE_ADDR, 0, MMC_RSP_R6, &uLong, sizeof(uLong));
    319 	if (iStatus) {
    320            printk("%s %d command number: %d failed\n", __FUNCTION__, __LINE__, SD_IO_SEND_RELATIVE_ADDR);
    321            return iStatus;
    322         }
    323 	iStatus = sdioDrv_ExecuteCmd (SD_IO_SELECT_CARD, uLong, MMC_RSP_R6, &uByte, sizeof(uByte));
    324 	if (iStatus) {
    325            printk("%s %d command number: %d failed\n", __FUNCTION__, __LINE__, SD_IO_SELECT_CARD);
    326            return iStatus;
    327         }
    328 
    329     /* NOTE:
    330      * =====
    331      * Each of the following loops is a workaround for a HW bug that will be solved in PG1.1 !!
    332      * Each write of CMD-52 to function-0 should use it as follows:
    333      * 1) Write the desired byte using CMD-52
    334      * 2) Read back the byte using CMD-52
    335      * 3) Write two dummy bytes to address 0xC8 using CMD-53
    336      * 4) If the byte read in step 2 is different than the written byte repeat the sequence
    337      */
    338 
    339     /* set device side bus width to 4 bit (for 1 bit write 0x80 instead of 0x82) */
    340     do
    341     {
    342         uByte = SDIO_BITS_CODE;
    343         iStatus = sdioDrv_WriteSyncBytes (TXN_FUNC_ID_CTRL, CCCR_BUS_INTERFACE_CONTOROL, &uByte, 1, 1);
    344         if (iStatus) { return iStatus; }
    345 
    346         iStatus = sdioDrv_ReadSyncBytes (TXN_FUNC_ID_CTRL, CCCR_BUS_INTERFACE_CONTOROL, &uByte, 1, 1);
    347         if (iStatus) { return iStatus; }
    348 
    349         iStatus = sdioDrv_WriteSync (TXN_FUNC_ID_CTRL, 0xC8, &uLong, 2, 1, 1);
    350         if (iStatus) { return iStatus; }
    351 
    352         uCount++;
    353 
    354     } while ((uByte != SDIO_BITS_CODE) && (uCount < MAX_RETRIES));
    355 
    356 
    357     uCount = 0;
    358 
    359     /* allow function 2 */
    360     do
    361     {
    362         uByte = 4;
    363         iStatus = sdioDrv_WriteSyncBytes (TXN_FUNC_ID_CTRL, CCCR_IO_ENABLE, &uByte, 1, 1);
    364         if (iStatus) { return iStatus; }
    365 
    366         iStatus = sdioDrv_ReadSyncBytes (TXN_FUNC_ID_CTRL, CCCR_IO_ENABLE, &uByte, 1, 1);
    367         if (iStatus) { return iStatus; }
    368 
    369         iStatus = sdioDrv_WriteSync (TXN_FUNC_ID_CTRL, 0xC8, &uLong, 2, 1, 1);
    370         if (iStatus) { return iStatus; }
    371 
    372         uCount++;
    373 
    374     } while ((uByte != 4) && (uCount < MAX_RETRIES));
    375 
    376 
    377 #ifdef SDIO_IN_BAND_INTERRUPT
    378 
    379     uCount = 0;
    380 
    381     do
    382     {
    383         uByte = 3;
    384         iStatus = sdioDrv_WriteSyncBytes (TXN_FUNC_ID_CTRL, CCCR_INT_ENABLE, &uByte, 1, 1);
    385         if (iStatus) { return iStatus; }
    386 
    387         iStatus = sdioDrv_ReadSyncBytes (TXN_FUNC_ID_CTRL, CCCR_INT_ENABLE, &uByte, 1, 1);
    388         if (iStatus) { return iStatus; }
    389 
    390         iStatus = sdioDrv_WriteSync (TXN_FUNC_ID_CTRL, 0xC8, &uLong, 2, 1, 1);
    391         if (iStatus) { return iStatus; }
    392 
    393         uCount++;
    394 
    395     } while ((uByte != 3) && (uCount < MAX_RETRIES));
    396 
    397 
    398 #endif
    399 
    400     uCount = 0;
    401 
    402     /* set block size for SDIO block mode */
    403     do
    404     {
    405         uLong = uBlkSize;
    406         iStatus = sdioDrv_WriteSync (TXN_FUNC_ID_CTRL, FN0_FBR2_REG_108, &uLong, 2, 1, 1);
    407         if (iStatus) { return iStatus; }
    408 
    409         iStatus = sdioDrv_ReadSync (TXN_FUNC_ID_CTRL, FN0_FBR2_REG_108, &uLong, 2, 1, 1);
    410         if (iStatus) { return iStatus; }
    411 
    412         iStatus = sdioDrv_WriteSync (TXN_FUNC_ID_CTRL, 0xC8, &uLong, 2, 1, 1);
    413         if (iStatus) { return iStatus; }
    414 
    415         uCount++;
    416 
    417     } while (((uLong & FN0_FBR2_REG_108_BIT_MASK) != uBlkSize) && (uCount < MAX_RETRIES));
    418 
    419 
    420     if (uCount >= MAX_RETRIES)
    421     {
    422         /* Failed to write CMD52_WRITE to function 0 */
    423         return (int)uCount;
    424     }
    425 
    426 	return iStatus;
    427 }
    428 
    429 
    430 int sdioAdapt_DisconnectBus (void)
    431 {
    432     return sdioDrv_DisconnectBus ();
    433 }
    434 
    435 ETxnStatus sdioAdapt_Transact (unsigned int  uFuncId,
    436                                unsigned int  uHwAddr,
    437                                void *        pHostAddr,
    438                                unsigned int  uLength,
    439                                unsigned int  bDirection,
    440                                unsigned int  bBlkMode,
    441                                unsigned int  bFixedAddr,
    442                                unsigned int  bMore)
    443 {
    444     int iStatus;
    445 
    446     /* If transction length is below threshold, use Sync methods */
    447     if (uLength < SYNC_ASYNC_LENGTH_THRESH)
    448     {
    449         /* Call read or write Sync method */
    450         if (bDirection)
    451         {
    452             CL_TRACE_START_L2();
    453             iStatus = sdioDrv_ReadSync (uFuncId, uHwAddr, pHostAddr, uLength, bFixedAddr, bMore);
    454             CL_TRACE_END_L2("tiwlan_drv.ko", "INHERIT", "SDIO", ".ReadSync");
    455         }
    456         else
    457         {
    458             CL_TRACE_START_L2();
    459             iStatus = sdioDrv_WriteSync (uFuncId, uHwAddr, pHostAddr, uLength, bFixedAddr, bMore);
    460             CL_TRACE_END_L2("tiwlan_drv.ko", "INHERIT", "SDIO", ".WriteSync");
    461         }
    462 
    463         /* If failed return ERROR, if succeeded return COMPLETE */
    464         if (iStatus)
    465         {
    466             return TXN_STATUS_ERROR;
    467         }
    468         return TXN_STATUS_COMPLETE;
    469     }
    470 
    471     /* If transction length is above threshold, use Async methods */
    472     else
    473     {
    474         /* Call read or write Async method */
    475         if (bDirection)
    476         {
    477             CL_TRACE_START_L2();
    478             iStatus = sdioDrv_ReadAsync (uFuncId, uHwAddr, pHostAddr, uLength, bBlkMode, bFixedAddr, bMore);
    479             CL_TRACE_END_L2("tiwlan_drv.ko", "INHERIT", "SDIO", ".ReadAsync");
    480         }
    481         else
    482         {
    483             CL_TRACE_START_L2();
    484             iStatus = sdioDrv_WriteAsync (uFuncId, uHwAddr, pHostAddr, uLength, bBlkMode, bFixedAddr, bMore);
    485             CL_TRACE_END_L2("tiwlan_drv.ko", "INHERIT", "SDIO", ".WriteAsync");
    486         }
    487 
    488         /* If failed return ERROR, if succeeded return PENDING */
    489         if (iStatus)
    490         {
    491             return TXN_STATUS_ERROR;
    492         }
    493         return TXN_STATUS_PENDING;
    494     }
    495 }
    496 
    497 ETxnStatus sdioAdapt_TransactBytes (unsigned int  uFuncId,
    498                                     unsigned int  uHwAddr,
    499                                     void *        pHostAddr,
    500                                     unsigned int  uLength,
    501                                     unsigned int  bDirection,
    502                                     unsigned int  bMore)
    503 {
    504     static unsigned int lastMore = 0;
    505     int iStatus;
    506 
    507     if ((bMore == 1) || (lastMore == bMore))
    508     {
    509         sdioDrv_clk_enable();
    510     }
    511 
    512     /* Call read or write bytes Sync method */
    513     if (bDirection)
    514     {
    515         iStatus = sdioDrv_ReadSyncBytes (uFuncId, uHwAddr, pHostAddr, uLength, bMore);
    516     }
    517     else
    518     {
    519         iStatus = sdioDrv_WriteSyncBytes (uFuncId, uHwAddr, pHostAddr, uLength, bMore);
    520     }
    521 
    522     if (bMore == 0)
    523     {
    524         sdioDrv_clk_disable();
    525     }
    526     lastMore = bMore;
    527 
    528     /* If failed return ERROR, if succeeded return COMPLETE */
    529     if (iStatus)
    530     {
    531         return TXN_STATUS_ERROR;
    532     }
    533     return TXN_STATUS_COMPLETE;
    534 }
    535 #endif
    536