blob: 7516d913dab383e24877d73538429ffa9314a9ca [file] [log] [blame]
Vipin Mehta30295c82010-09-01 12:06:33 -07001//------------------------------------------------------------------------------
2// Copyright (c) 2009-2010 Atheros Corporation. All rights reserved.
3//
4//
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.
8//
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.
16//
17//
18//------------------------------------------------------------------------------
19//==============================================================================
20// HIF scatter implementation
21//
22// Author(s): ="Atheros"
23//==============================================================================
24
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
33#include "a_debug.h"
34
35#ifdef HIF_LINUX_MMC_SCATTER_SUPPORT
36
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
42
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)
50
Luis R. Rodriguez9dabb722011-03-14 10:59:12 -070051static void FreeScatterReq(struct hif_device *device, struct hif_scatter_req *pReq)
Vipin Mehta30295c82010-09-01 12:06:33 -070052{
53 unsigned long flag;
54
55 spin_lock_irqsave(&device->lock, flag);
56
57 DL_ListInsertTail(&device->ScatterReqHead, &pReq->ListLink);
58
59 spin_unlock_irqrestore(&device->lock, flag);
60
61}
62
Luis R. Rodriguez9dabb722011-03-14 10:59:12 -070063static struct hif_scatter_req *AllocScatterReq(struct hif_device *device)
Vipin Mehta30295c82010-09-01 12:06:33 -070064{
Luis R. Rodriguez01eb1da2011-03-14 10:58:35 -070065 struct dl_list *pItem;
Vipin Mehta30295c82010-09-01 12:06:33 -070066 unsigned long flag;
67
68 spin_lock_irqsave(&device->lock, flag);
69
70 pItem = DL_ListRemoveItemFromHead(&device->ScatterReqHead);
71
72 spin_unlock_irqrestore(&device->lock, flag);
73
74 if (pItem != NULL) {
Luis R. Rodriguez0aaabb82011-03-14 10:58:49 -070075 return A_CONTAINING_STRUCT(pItem, struct hif_scatter_req, ListLink);
Vipin Mehta30295c82010-09-01 12:06:33 -070076 }
77
78 return NULL;
79}
80
81 /* called by async task to perform the operation synchronously using direct MMC APIs */
Luis R. Rodriguez9dabb722011-03-14 10:59:12 -070082int DoHifReadWriteScatter(struct hif_device *device, BUS_REQUEST *busrequest)
Vipin Mehta30295c82010-09-01 12:06:33 -070083{
84 int i;
Joe Perchesab3655d2011-02-02 14:05:49 -080085 u8 rw;
86 u8 opcode;
Vipin Mehta30295c82010-09-01 12:06:33 -070087 struct mmc_request mmcreq;
88 struct mmc_command cmd;
89 struct mmc_data data;
Luis R. Rodriguez7038aac2011-03-14 10:58:48 -070090 struct hif_scatter_req_priv *pReqPriv;
Luis R. Rodriguez0aaabb82011-03-14 10:58:49 -070091 struct hif_scatter_req *pReq;
Joe Perches4f69cef2011-02-02 14:05:57 -080092 int status = 0;
Vipin Mehta30295c82010-09-01 12:06:33 -070093 struct scatterlist *pSg;
94
95 pReqPriv = busrequest->pScatterReq;
96
97 A_ASSERT(pReqPriv != NULL);
98
99 pReq = pReqPriv->pHifScatterReq;
100
101 memset(&mmcreq, 0, sizeof(struct mmc_request));
102 memset(&cmd, 0, sizeof(struct mmc_command));
103 memset(&data, 0, sizeof(struct mmc_data));
104
105 data.blksz = HIF_MBOX_BLOCK_SIZE;
106 data.blocks = pReq->TotalLength / HIF_MBOX_BLOCK_SIZE;
107
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));
111
112 if (pReq->Request & HIF_WRITE) {
113 rw = _CMD53_ARG_WRITE;
114 data.flags = MMC_DATA_WRITE;
115 } else {
116 rw = _CMD53_ARG_READ;
117 data.flags = MMC_DATA_READ;
118 }
119
120 if (pReq->Request & HIF_FIXED_ADDRESS) {
121 opcode = _CMD53_ARG_FIXED_ADDRESS;
122 } else {
123 opcode = _CMD53_ARG_INCR_ADDRESS;
124 }
125
126 /* fill SG entries */
127 pSg = pReqPriv->sgentries;
128 sg_init_table(pSg, pReq->ValidScatterEntries);
129
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));
140 }
141
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));
144
145 sg_set_buf(pSg, pReq->ScatterList[i].pBuffer, pReq->ScatterList[i].Length);
146 }
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,
152 rw,
153 device->func->num,
154 _CMD53_ARG_BLOCK_BASIS,
155 opcode,
156 pReq->Address,
157 data.blocks);
158
159 cmd.opcode = SD_IO_RW_EXTENDED;
160 cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
161
162 mmcreq.cmd = &cmd;
163 mmcreq.data = &data;
164
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);
168
169 if (cmd.error) {
170 status = A_ERROR;
171 AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("HIF-SCATTER: cmd error: %d \n",cmd.error));
172 }
173
174 if (data.error) {
175 status = A_ERROR;
176 AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("HIF-SCATTER: data error: %d \n",data.error));
177 }
178
Joe Perches391bb212011-01-27 20:04:21 -0800179 if (status) {
Vipin Mehta30295c82010-09-01 12:06:33 -0700180 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));
182 }
183
184 /* set completion status, fail or success */
185 pReq->CompletionStatus = status;
186
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);
192 } else {
193 AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, ("HIF-SCATTER async_task upping busrequest : 0x%lX (%d)\n", (unsigned long)busrequest,status));
194 /* signal wait */
195 up(&busrequest->sem_req);
196 }
197
198 return status;
199}
200
201 /* callback to issue a read-write scatter request */
Luis R. Rodriguez9dabb722011-03-14 10:59:12 -0700202static int HifReadWriteScatter(struct hif_device *device, struct hif_scatter_req *pReq)
Vipin Mehta30295c82010-09-01 12:06:33 -0700203{
Joe Perches1f4c34b2011-01-27 20:04:19 -0800204 int status = A_EINVAL;
Joe Perchese1ce2a32011-02-02 14:05:51 -0800205 u32 request = pReq->Request;
Luis R. Rodriguez7038aac2011-03-14 10:58:48 -0700206 struct hif_scatter_req_priv *pReqPriv = (struct hif_scatter_req_priv *)pReq->HIFPrivate[0];
Vipin Mehta30295c82010-09-01 12:06:33 -0700207
208 do {
209
210 A_ASSERT(pReqPriv != NULL);
211
212 AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, ("HIF-SCATTER: total len: %d Scatter Entries: %d\n",
213 pReq->TotalLength, pReq->ValidScatterEntries));
214
215 if (!(request & HIF_EXTENDED_IO)) {
216 AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
217 ("HIF-SCATTER: Invalid command type: 0x%08x\n", request));
218 break;
219 }
220
221 if (!(request & (HIF_SYNCHRONOUS | HIF_ASYNCHRONOUS))) {
222 AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
223 ("HIF-SCATTER: Invalid execution mode: 0x%08x\n", request));
224 break;
225 }
226
227 if (!(request & HIF_BLOCK_BASIS)) {
228 AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
229 ("HIF-SCATTER: Invalid data mode: 0x%08x\n", request));
230 break;
231 }
232
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));
236 break;
237 }
238
239 if (pReq->TotalLength == 0) {
Joe Perches1071a132011-02-02 14:05:47 -0800240 A_ASSERT(false);
Vipin Mehta30295c82010-09-01 12:06:33 -0700241 break;
242 }
243
244 /* add bus request to the async list for the async I/O thread to process */
245 AddToAsyncList(device, pReqPriv->busrequest);
246
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 */
254 status = A_ERROR;
255 break;
256 } else {
257 status = pReq->CompletionStatus;
258 }
259 } else {
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);
Joe Perches4f69cef2011-02-02 14:05:57 -0800263 status = 0;
Vipin Mehta30295c82010-09-01 12:06:33 -0700264 }
265
Joe Perches1071a132011-02-02 14:05:47 -0800266 } while (false);
Vipin Mehta30295c82010-09-01 12:06:33 -0700267
Joe Perches391bb212011-01-27 20:04:21 -0800268 if (status && (request & HIF_ASYNCHRONOUS)) {
Vipin Mehta30295c82010-09-01 12:06:33 -0700269 pReq->CompletionStatus = status;
270 pReq->CompletionRoutine(pReq);
Joe Perches4f69cef2011-02-02 14:05:57 -0800271 status = 0;
Vipin Mehta30295c82010-09-01 12:06:33 -0700272 }
273
274 return status;
275}
276
277 /* setup of HIF scatter resources */
Luis R. Rodriguez9dabb722011-03-14 10:59:12 -0700278int SetupHIFScatterSupport(struct hif_device *device, struct hif_device_scatter_support_info *pInfo)
Vipin Mehta30295c82010-09-01 12:06:33 -0700279{
Joe Perches1f4c34b2011-01-27 20:04:19 -0800280 int status = A_ERROR;
Vipin Mehta30295c82010-09-01 12:06:33 -0700281 int i;
Luis R. Rodriguez7038aac2011-03-14 10:58:48 -0700282 struct hif_scatter_req_priv *pReqPriv;
Vipin Mehta30295c82010-09-01 12:06:33 -0700283 BUS_REQUEST *busrequest;
284
285 do {
286
287 /* check if host supports scatter requests and it meets our requirements */
Vipin Mehta73bb2f22010-09-17 18:45:38 -0700288 if (device->func->card->host->max_segs < MAX_SCATTER_ENTRIES_PER_REQ) {
Vipin Mehta30295c82010-09-01 12:06:33 -0700289 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HIF-SCATTER : host only supports scatter of : %d entries, need: %d \n",
Vipin Mehta73bb2f22010-09-17 18:45:38 -0700290 device->func->card->host->max_segs, MAX_SCATTER_ENTRIES_PER_REQ));
Vipin Mehta30295c82010-09-01 12:06:33 -0700291 status = A_ENOTSUP;
292 break;
293 }
294
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));
297
298 for (i = 0; i < MAX_SCATTER_REQUESTS; i++) {
299 /* allocate the private request blob */
Luis R. Rodriguez7038aac2011-03-14 10:58:48 -0700300 pReqPriv = (struct hif_scatter_req_priv *)A_MALLOC(sizeof(struct hif_scatter_req_priv));
Vipin Mehta30295c82010-09-01 12:06:33 -0700301 if (NULL == pReqPriv) {
302 break;
303 }
Luis R. Rodriguez7038aac2011-03-14 10:58:48 -0700304 A_MEMZERO(pReqPriv, sizeof(struct hif_scatter_req_priv));
Vipin Mehta30295c82010-09-01 12:06:33 -0700305 /* save the device instance*/
306 pReqPriv->device = device;
307 /* allocate the scatter request */
Luis R. Rodriguez0aaabb82011-03-14 10:58:49 -0700308 pReqPriv->pHifScatterReq = (struct hif_scatter_req *)A_MALLOC(sizeof(struct hif_scatter_req) +
Luis R. Rodriguezf88902c2011-03-14 10:58:47 -0700309 (MAX_SCATTER_ENTRIES_PER_REQ - 1) * (sizeof(struct hif_scatter_item)));
Vipin Mehta30295c82010-09-01 12:06:33 -0700310
311 if (NULL == pReqPriv->pHifScatterReq) {
Luis R. Rodriguezab891112011-03-29 17:55:58 -0700312 kfree(pReqPriv);
Vipin Mehta30295c82010-09-01 12:06:33 -0700313 break;
314 }
315 /* just zero the main part of the scatter request */
Luis R. Rodriguez0aaabb82011-03-14 10:58:49 -0700316 A_MEMZERO(pReqPriv->pHifScatterReq, sizeof(struct hif_scatter_req));
Vipin Mehta30295c82010-09-01 12:06:33 -0700317 /* 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) {
Luis R. Rodriguezab891112011-03-29 17:55:58 -0700322 kfree(pReqPriv->pHifScatterReq);
323 kfree(pReqPriv);
Vipin Mehta30295c82010-09-01 12:06:33 -0700324 break;
325 }
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);
332 }
333
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"));
337 break;
338 }
339
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;
346
Joe Perches4f69cef2011-02-02 14:05:57 -0800347 status = 0;
Vipin Mehta30295c82010-09-01 12:06:33 -0700348
Joe Perches1071a132011-02-02 14:05:47 -0800349 } while (false);
Vipin Mehta30295c82010-09-01 12:06:33 -0700350
Joe Perches391bb212011-01-27 20:04:21 -0800351 if (status) {
Vipin Mehta30295c82010-09-01 12:06:33 -0700352 CleanupHIFScatterResources(device);
353 }
354
355 return status;
356}
357
358 /* clean up scatter support */
Luis R. Rodriguez9dabb722011-03-14 10:59:12 -0700359void CleanupHIFScatterResources(struct hif_device *device)
Vipin Mehta30295c82010-09-01 12:06:33 -0700360{
Luis R. Rodriguez7038aac2011-03-14 10:58:48 -0700361 struct hif_scatter_req_priv *pReqPriv;
Luis R. Rodriguez0aaabb82011-03-14 10:58:49 -0700362 struct hif_scatter_req *pReq;
Vipin Mehta30295c82010-09-01 12:06:33 -0700363
364 /* empty the free list */
365
366 while (1) {
367
368 pReq = AllocScatterReq(device);
369
370 if (NULL == pReq) {
371 break;
372 }
373
Luis R. Rodriguez7038aac2011-03-14 10:58:48 -0700374 pReqPriv = (struct hif_scatter_req_priv *)pReq->HIFPrivate[0];
Vipin Mehta30295c82010-09-01 12:06:33 -0700375 A_ASSERT(pReqPriv != NULL);
376
377 if (pReqPriv->busrequest != NULL) {
378 pReqPriv->busrequest->pScatterReq = NULL;
379 /* free bus request */
380 hifFreeBusRequest(device, pReqPriv->busrequest);
381 pReqPriv->busrequest = NULL;
382 }
383
384 if (pReqPriv->pHifScatterReq != NULL) {
Luis R. Rodriguezab891112011-03-29 17:55:58 -0700385 kfree(pReqPriv->pHifScatterReq);
Vipin Mehta30295c82010-09-01 12:06:33 -0700386 pReqPriv->pHifScatterReq = NULL;
387 }
388
Luis R. Rodriguezab891112011-03-29 17:55:58 -0700389 kfree(pReqPriv);
Vipin Mehta30295c82010-09-01 12:06:33 -0700390 }
391}
392
393#endif // HIF_LINUX_MMC_SCATTER_SUPPORT