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
sdio_wifi_probe(struct sdio_func * func,const struct sdio_device_id * id)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
sdio_wifi_remove(struct sdio_func * func)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
sdioAdapt_ConnectBus(void * fCbFunc,void * hCbArg,unsigned int uBlkSizeShift,unsigned int uSdioThreadPriority,unsigned char ** pTxDmaSrcAddr)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
sdioAdapt_DisconnectBus(void)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
sdioAdapt_TransactBytes(unsigned int uFuncId,unsigned int uHwAddr,void * pHostAddr,unsigned int uLength,unsigned int bDirection,unsigned int bMore)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
sdioAdapt_Transact(unsigned int uFuncId,unsigned int uHwAddr,void * pHostAddr,unsigned int uLength,unsigned int bDirection,unsigned int bBlkMode,unsigned int bFixedAddr,unsigned int bMore)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
sdioAdapt_ConnectBus(void * fCbFunc,void * hCbArg,unsigned int uBlkSizeShift,unsigned int uSdioThreadPriority,unsigned char ** pTxDmaSrcAddr)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
sdioAdapt_DisconnectBus(void)430 int sdioAdapt_DisconnectBus (void)
431 {
432 return sdioDrv_DisconnectBus ();
433 }
434
sdioAdapt_Transact(unsigned int uFuncId,unsigned int uHwAddr,void * pHostAddr,unsigned int uLength,unsigned int bDirection,unsigned int bBlkMode,unsigned int bFixedAddr,unsigned int bMore)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
sdioAdapt_TransactBytes(unsigned int uFuncId,unsigned int uHwAddr,void * pHostAddr,unsigned int uLength,unsigned int bDirection,unsigned int bMore)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