1 //------------------------------------------------------------------------------
2 // Copyright (c) 2009-2010 Atheros Corporation. All rights reserved.
5 // Permission to use, copy, modify, and/or distribute this software for any
6 // purpose with or without fee is hereby granted, provided that the above
7 // copyright notice and this permission notice appear in all copies.
9 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 //------------------------------------------------------------------------------
19 //==============================================================================
20 // HIF scatter implementation
22 // Author(s): ="Atheros"
23 //==============================================================================
25 #include <linux/mmc/card.h>
26 #include <linux/mmc/host.h>
27 #include <linux/mmc/sdio_func.h>
28 #include <linux/mmc/sdio_ids.h>
29 #include <linux/mmc/sdio.h>
30 #include <linux/kthread.h>
31 #include "hif_internal.h"
32 #define ATH_MODULE_NAME hif
35 #ifdef HIF_LINUX_MMC_SCATTER_SUPPORT
37 #define _CMD53_ARG_READ 0
38 #define _CMD53_ARG_WRITE 1
39 #define _CMD53_ARG_BLOCK_BASIS 1
40 #define _CMD53_ARG_FIXED_ADDRESS 0
41 #define _CMD53_ARG_INCR_ADDRESS 1
43 #define SDIO_SET_CMD53_ARG(arg,rw,func,mode,opcode,address,bytes_blocks) \
44 (arg) = (((rw) & 1) << 31) | \
45 (((func) & 0x7) << 28) | \
46 (((mode) & 1) << 27) | \
47 (((opcode) & 1) << 26) | \
48 (((address) & 0x1FFFF) << 9) | \
49 ((bytes_blocks) & 0x1FF)
51 static void FreeScatterReq(HIF_DEVICE
*device
, HIF_SCATTER_REQ
*pReq
)
55 spin_lock_irqsave(&device
->lock
, flag
);
57 DL_ListInsertTail(&device
->ScatterReqHead
, &pReq
->ListLink
);
59 spin_unlock_irqrestore(&device
->lock
, flag
);
63 static HIF_SCATTER_REQ
*AllocScatterReq(HIF_DEVICE
*device
)
68 spin_lock_irqsave(&device
->lock
, flag
);
70 pItem
= DL_ListRemoveItemFromHead(&device
->ScatterReqHead
);
72 spin_unlock_irqrestore(&device
->lock
, flag
);
75 return A_CONTAINING_STRUCT(pItem
, HIF_SCATTER_REQ
, ListLink
);
81 /* called by async task to perform the operation synchronously using direct MMC APIs */
82 int DoHifReadWriteScatter(HIF_DEVICE
*device
, BUS_REQUEST
*busrequest
)
87 struct mmc_request mmcreq
;
88 struct mmc_command cmd
;
90 HIF_SCATTER_REQ_PRIV
*pReqPriv
;
91 HIF_SCATTER_REQ
*pReq
;
93 struct scatterlist
*pSg
;
95 pReqPriv
= busrequest
->pScatterReq
;
97 A_ASSERT(pReqPriv
!= NULL
);
99 pReq
= pReqPriv
->pHifScatterReq
;
101 memset(&mmcreq
, 0, sizeof(struct mmc_request
));
102 memset(&cmd
, 0, sizeof(struct mmc_command
));
103 memset(&data
, 0, sizeof(struct mmc_data
));
105 data
.blksz
= HIF_MBOX_BLOCK_SIZE
;
106 data
.blocks
= pReq
->TotalLength
/ HIF_MBOX_BLOCK_SIZE
;
108 AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER
, ("HIF-SCATTER: (%s) Address: 0x%X, (BlockLen: %d, BlockCount: %d) , (tot:%d,sg:%d)\n",
109 (pReq
->Request
& HIF_WRITE
) ? "WRITE":"READ", pReq
->Address
, data
.blksz
, data
.blocks
,
110 pReq
->TotalLength
,pReq
->ValidScatterEntries
));
112 if (pReq
->Request
& HIF_WRITE
) {
113 rw
= _CMD53_ARG_WRITE
;
114 data
.flags
= MMC_DATA_WRITE
;
116 rw
= _CMD53_ARG_READ
;
117 data
.flags
= MMC_DATA_READ
;
120 if (pReq
->Request
& HIF_FIXED_ADDRESS
) {
121 opcode
= _CMD53_ARG_FIXED_ADDRESS
;
123 opcode
= _CMD53_ARG_INCR_ADDRESS
;
126 /* fill SG entries */
127 pSg
= pReqPriv
->sgentries
;
128 sg_init_table(pSg
, pReq
->ValidScatterEntries
);
130 /* assemble SG list */
131 for (i
= 0 ; i
< pReq
->ValidScatterEntries
; i
++, pSg
++) {
132 /* setup each sg entry */
133 if ((unsigned long)pReq
->ScatterList
[i
].pBuffer
& 0x3) {
134 /* note some scatter engines can handle unaligned buffers, print this
135 * as informational only */
136 AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER
,
137 ("HIF: (%s) Scatter Buffer is unaligned 0x%lx\n",
138 pReq
->Request
& HIF_WRITE
? "WRITE":"READ",
139 (unsigned long)pReq
->ScatterList
[i
].pBuffer
));
142 AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER
, (" %d: Addr:0x%lX, Len:%d \n",
143 i
,(unsigned long)pReq
->ScatterList
[i
].pBuffer
,pReq
->ScatterList
[i
].Length
));
145 sg_set_buf(pSg
, pReq
->ScatterList
[i
].pBuffer
, pReq
->ScatterList
[i
].Length
);
147 /* set scatter-gather table for request */
148 data
.sg
= pReqPriv
->sgentries
;
149 data
.sg_len
= pReq
->ValidScatterEntries
;
150 /* set command argument */
151 SDIO_SET_CMD53_ARG(cmd
.arg
,
154 _CMD53_ARG_BLOCK_BASIS
,
159 cmd
.opcode
= SD_IO_RW_EXTENDED
;
160 cmd
.flags
= MMC_RSP_SPI_R5
| MMC_RSP_R5
| MMC_CMD_ADTC
;
165 mmc_set_data_timeout(&data
, device
->func
->card
);
166 /* synchronous call to process request */
167 mmc_wait_for_req(device
->func
->card
->host
, &mmcreq
);
171 AR_DEBUG_PRINTF(ATH_DEBUG_ERROR
, ("HIF-SCATTER: cmd error: %d \n",cmd
.error
));
176 AR_DEBUG_PRINTF(ATH_DEBUG_ERROR
, ("HIF-SCATTER: data error: %d \n",data
.error
));
179 if (A_FAILED(status
)) {
180 AR_DEBUG_PRINTF(ATH_DEBUG_ERROR
, ("HIF-SCATTER: FAILED!!! (%s) Address: 0x%X, Block mode (BlockLen: %d, BlockCount: %d)\n",
181 (pReq
->Request
& HIF_WRITE
) ? "WRITE":"READ",pReq
->Address
, data
.blksz
, data
.blocks
));
184 /* set completion status, fail or success */
185 pReq
->CompletionStatus
= status
;
187 if (pReq
->Request
& HIF_ASYNCHRONOUS
) {
188 AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER
, ("HIF-SCATTER: async_task completion routine req: 0x%lX (%d)\n",(unsigned long)busrequest
, status
));
189 /* complete the request */
190 A_ASSERT(pReq
->CompletionRoutine
!= NULL
);
191 pReq
->CompletionRoutine(pReq
);
193 AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER
, ("HIF-SCATTER async_task upping busrequest : 0x%lX (%d)\n", (unsigned long)busrequest
,status
));
195 up(&busrequest
->sem_req
);
201 /* callback to issue a read-write scatter request */
202 static int HifReadWriteScatter(HIF_DEVICE
*device
, HIF_SCATTER_REQ
*pReq
)
204 int status
= A_EINVAL
;
205 A_UINT32 request
= pReq
->Request
;
206 HIF_SCATTER_REQ_PRIV
*pReqPriv
= (HIF_SCATTER_REQ_PRIV
*)pReq
->HIFPrivate
[0];
210 A_ASSERT(pReqPriv
!= NULL
);
212 AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER
, ("HIF-SCATTER: total len: %d Scatter Entries: %d\n",
213 pReq
->TotalLength
, pReq
->ValidScatterEntries
));
215 if (!(request
& HIF_EXTENDED_IO
)) {
216 AR_DEBUG_PRINTF(ATH_DEBUG_ERROR
,
217 ("HIF-SCATTER: Invalid command type: 0x%08x\n", request
));
221 if (!(request
& (HIF_SYNCHRONOUS
| HIF_ASYNCHRONOUS
))) {
222 AR_DEBUG_PRINTF(ATH_DEBUG_ERROR
,
223 ("HIF-SCATTER: Invalid execution mode: 0x%08x\n", request
));
227 if (!(request
& HIF_BLOCK_BASIS
)) {
228 AR_DEBUG_PRINTF(ATH_DEBUG_ERROR
,
229 ("HIF-SCATTER: Invalid data mode: 0x%08x\n", request
));
233 if (pReq
->TotalLength
> MAX_SCATTER_REQ_TRANSFER_SIZE
) {
234 AR_DEBUG_PRINTF(ATH_DEBUG_ERROR
,
235 ("HIF-SCATTER: Invalid length: %d \n", pReq
->TotalLength
));
239 if (pReq
->TotalLength
== 0) {
244 /* add bus request to the async list for the async I/O thread to process */
245 AddToAsyncList(device
, pReqPriv
->busrequest
);
247 if (request
& HIF_SYNCHRONOUS
) {
248 AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER
, ("HIF-SCATTER: queued sync req: 0x%lX\n", (unsigned long)pReqPriv
->busrequest
));
249 /* signal thread and wait */
250 up(&device
->sem_async
);
251 if (down_interruptible(&pReqPriv
->busrequest
->sem_req
) != 0) {
252 AR_DEBUG_PRINTF(ATH_DEBUG_ERROR
,("HIF-SCATTER: interrupted! \n"));
253 /* interrupted, exit */
257 status
= pReq
->CompletionStatus
;
260 AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER
, ("HIF-SCATTER: queued async req: 0x%lX\n", (unsigned long)pReqPriv
->busrequest
));
261 /* wake thread, it will process and then take care of the async callback */
262 up(&device
->sem_async
);
268 if (A_FAILED(status
) && (request
& HIF_ASYNCHRONOUS
)) {
269 pReq
->CompletionStatus
= status
;
270 pReq
->CompletionRoutine(pReq
);
277 /* setup of HIF scatter resources */
278 int SetupHIFScatterSupport(HIF_DEVICE
*device
, HIF_DEVICE_SCATTER_SUPPORT_INFO
*pInfo
)
280 int status
= A_ERROR
;
282 HIF_SCATTER_REQ_PRIV
*pReqPriv
;
283 BUS_REQUEST
*busrequest
;
287 /* check if host supports scatter requests and it meets our requirements */
288 if (device
->func
->card
->host
->max_segs
< MAX_SCATTER_ENTRIES_PER_REQ
) {
289 AR_DEBUG_PRINTF(ATH_DEBUG_ERR
,("HIF-SCATTER : host only supports scatter of : %d entries, need: %d \n",
290 device
->func
->card
->host
->max_segs
, MAX_SCATTER_ENTRIES_PER_REQ
));
295 AR_DEBUG_PRINTF(ATH_DEBUG_ANY
,("HIF-SCATTER Enabled: max scatter req : %d entries: %d \n",
296 MAX_SCATTER_REQUESTS
, MAX_SCATTER_ENTRIES_PER_REQ
));
298 for (i
= 0; i
< MAX_SCATTER_REQUESTS
; i
++) {
299 /* allocate the private request blob */
300 pReqPriv
= (HIF_SCATTER_REQ_PRIV
*)A_MALLOC(sizeof(HIF_SCATTER_REQ_PRIV
));
301 if (NULL
== pReqPriv
) {
304 A_MEMZERO(pReqPriv
, sizeof(HIF_SCATTER_REQ_PRIV
));
305 /* save the device instance*/
306 pReqPriv
->device
= device
;
307 /* allocate the scatter request */
308 pReqPriv
->pHifScatterReq
= (HIF_SCATTER_REQ
*)A_MALLOC(sizeof(HIF_SCATTER_REQ
) +
309 (MAX_SCATTER_ENTRIES_PER_REQ
- 1) * (sizeof(HIF_SCATTER_ITEM
)));
311 if (NULL
== pReqPriv
->pHifScatterReq
) {
315 /* just zero the main part of the scatter request */
316 A_MEMZERO(pReqPriv
->pHifScatterReq
, sizeof(HIF_SCATTER_REQ
));
317 /* back pointer to the private struct */
318 pReqPriv
->pHifScatterReq
->HIFPrivate
[0] = pReqPriv
;
319 /* allocate a bus request for this scatter request */
320 busrequest
= hifAllocateBusRequest(device
);
321 if (NULL
== busrequest
) {
322 A_FREE(pReqPriv
->pHifScatterReq
);
326 /* assign the scatter request to this bus request */
327 busrequest
->pScatterReq
= pReqPriv
;
328 /* point back to the request */
329 pReqPriv
->busrequest
= busrequest
;
330 /* add it to the scatter pool */
331 FreeScatterReq(device
,pReqPriv
->pHifScatterReq
);
334 if (i
!= MAX_SCATTER_REQUESTS
) {
335 status
= A_NO_MEMORY
;
336 AR_DEBUG_PRINTF(ATH_DEBUG_ERR
,("HIF-SCATTER : failed to alloc scatter resources !\n"));
340 /* set scatter function pointers */
341 pInfo
->pAllocateReqFunc
= AllocScatterReq
;
342 pInfo
->pFreeReqFunc
= FreeScatterReq
;
343 pInfo
->pReadWriteScatterFunc
= HifReadWriteScatter
;
344 pInfo
->MaxScatterEntries
= MAX_SCATTER_ENTRIES_PER_REQ
;
345 pInfo
->MaxTransferSizePerScatterReq
= MAX_SCATTER_REQ_TRANSFER_SIZE
;
351 if (A_FAILED(status
)) {
352 CleanupHIFScatterResources(device
);
358 /* clean up scatter support */
359 void CleanupHIFScatterResources(HIF_DEVICE
*device
)
361 HIF_SCATTER_REQ_PRIV
*pReqPriv
;
362 HIF_SCATTER_REQ
*pReq
;
364 /* empty the free list */
368 pReq
= AllocScatterReq(device
);
374 pReqPriv
= (HIF_SCATTER_REQ_PRIV
*)pReq
->HIFPrivate
[0];
375 A_ASSERT(pReqPriv
!= NULL
);
377 if (pReqPriv
->busrequest
!= NULL
) {
378 pReqPriv
->busrequest
->pScatterReq
= NULL
;
379 /* free bus request */
380 hifFreeBusRequest(device
, pReqPriv
->busrequest
);
381 pReqPriv
->busrequest
= NULL
;
384 if (pReqPriv
->pHifScatterReq
!= NULL
) {
385 A_FREE(pReqPriv
->pHifScatterReq
);
386 pReqPriv
->pHifScatterReq
= NULL
;
393 #endif // HIF_LINUX_MMC_SCATTER_SUPPORT