Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelv...
[pandora-kernel.git] / drivers / staging / ath6kl / hif / sdio / linux_sdio / src / hif_scatter.c
1 //------------------------------------------------------------------------------
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             
51 static void FreeScatterReq(HIF_DEVICE *device, HIF_SCATTER_REQ *pReq)
52 {   
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
63 static HIF_SCATTER_REQ *AllocScatterReq(HIF_DEVICE *device) 
64 {
65     DL_LIST       *pItem; 
66     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) {
75         return A_CONTAINING_STRUCT(pItem, HIF_SCATTER_REQ, ListLink);
76     }
77     
78     return NULL;   
79 }
80
81     /* called by async task to perform the operation synchronously using direct MMC APIs  */
82 A_STATUS DoHifReadWriteScatter(HIF_DEVICE *device, BUS_REQUEST *busrequest)
83 {
84     int                     i;
85     A_UINT8                 rw;
86     A_UINT8                 opcode;
87     struct mmc_request      mmcreq;
88     struct mmc_command      cmd;
89     struct mmc_data         data;
90     HIF_SCATTER_REQ_PRIV   *pReqPriv;   
91     HIF_SCATTER_REQ        *pReq;       
92     A_STATUS                status = A_OK;
93     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
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));        
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 */
202 static A_STATUS HifReadWriteScatter(HIF_DEVICE *device, HIF_SCATTER_REQ *pReq)
203 {
204     A_STATUS             status = A_EINVAL;
205     A_UINT32             request = pReq->Request;
206     HIF_SCATTER_REQ_PRIV *pReqPriv = (HIF_SCATTER_REQ_PRIV *)pReq->HIFPrivate[0];
207     
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) {
240             A_ASSERT(FALSE);
241             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);
263             status = A_OK;
264         }           
265        
266     } while (FALSE);
267
268     if (A_FAILED(status) && (request & HIF_ASYNCHRONOUS)) {
269         pReq->CompletionStatus = status;
270         pReq->CompletionRoutine(pReq);
271         status = A_OK;
272     }
273         
274     return status;  
275 }
276
277     /* setup of HIF scatter resources */
278 A_STATUS SetupHIFScatterSupport(HIF_DEVICE *device, HIF_DEVICE_SCATTER_SUPPORT_INFO *pInfo)
279 {
280     A_STATUS              status = A_ERROR;   
281     int                   i;
282     HIF_SCATTER_REQ_PRIV *pReqPriv;
283     BUS_REQUEST          *busrequest;
284         
285     do {
286         
287             /* check if host supports scatter requests and it meets our requirements */
288         if (device->func->card->host->max_hw_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_hw_segs, MAX_SCATTER_ENTRIES_PER_REQ));
291             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 */
300             pReqPriv = (HIF_SCATTER_REQ_PRIV *)A_MALLOC(sizeof(HIF_SCATTER_REQ_PRIV));
301             if (NULL == pReqPriv) {
302                 break;    
303             }
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))); 
310            
311             if (NULL == pReqPriv->pHifScatterReq) {
312                 A_FREE(pReqPriv);
313                 break;      
314             }           
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);
323                 A_FREE(pReqPriv);
324                 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      
347         status = A_OK;
348         
349     } while (FALSE);
350     
351     if (A_FAILED(status)) {
352         CleanupHIFScatterResources(device);   
353     }
354     
355     return status;
356 }
357
358     /* clean up scatter support */
359 void CleanupHIFScatterResources(HIF_DEVICE *device)
360 {
361     HIF_SCATTER_REQ_PRIV    *pReqPriv;
362     HIF_SCATTER_REQ         *pReq;
363     
364         /* empty the free list */
365         
366     while (1) {
367         
368         pReq = AllocScatterReq(device);
369                 
370         if (NULL == pReq) {
371             break;    
372         }   
373         
374         pReqPriv = (HIF_SCATTER_REQ_PRIV *)pReq->HIFPrivate[0];
375         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) {
385             A_FREE(pReqPriv->pHifScatterReq);   
386             pReqPriv->pHifScatterReq = NULL; 
387         }
388                 
389         A_FREE(pReqPriv);       
390     }
391 }
392
393 #endif // HIF_LINUX_MMC_SCATTER_SUPPORT